r190639 - PR13657 (and duplicates):

Richard Smith richard-llvm at metafoo.co.uk
Thu Sep 12 16:28:09 PDT 2013


Author: rsmith
Date: Thu Sep 12 18:28:08 2013
New Revision: 190639

URL: http://llvm.org/viewvc/llvm-project?rev=190639&view=rev
Log:
PR13657 (and duplicates):

When a comma occurs in a default argument or default initializer within a
class, disambiguate whether it is part of the initializer or whether it ends
the initializer.

The way this works (which I will be proposing for standardization) is to treat
the comma as ending the default argument or default initializer if the
following token sequence matches the syntactic constraints of a
parameter-declaration-clause or init-declarator-list (respectively).

This is both consistent with the disambiguation rules elsewhere (where entities
are treated as declarations if they can be), and should have no regressions
over our old behavior. I think it might also disambiguate all cases correctly,
but I don't have a proof of that.

There is an annoyance here: because we're performing a tentative parse in a
situation where we may not have seen declarations of all relevant entities (if
the comma is part of the initializer, lookup may find entites declared later in
the class), we need to turn off typo-correction and diagnostics during the
tentative parse, and in the rare case that we decide the comma is part of the
initializer, we need to revert all token annotations we performed while
disambiguating.

Any diagnostics that occur outside of the immediate context of the tentative
parse (for instance, if we trigger the implicit instantiation of a class
template) are *not* suppressed, mirroring the usual rules for a SFINAE context.

Added:
    cfe/trunk/test/Parser/cxx-ambig-init-templ.cpp
Modified:
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseTentative.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/test/Parser/cxx-default-args.cpp
    cfe/trunk/test/Parser/cxx0x-member-initializers.cpp

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Thu Sep 12 18:28:08 2013
@@ -608,6 +608,7 @@ private:
       assert(!isActive && "Forgot to call Commit or Revert!");
     }
   };
+  class UnannotatedTentativeParsingAction;
 
   /// ObjCDeclContextSwitch - An object used to switch context from
   /// an objective-c decl context to its enclosing decl context and
@@ -1061,6 +1062,11 @@ private:
   void DeallocateParsedClasses(ParsingClass *Class);
   void PopParsingClass(Sema::ParsingClassState);
 
+  enum CachedInitKind {
+    CIK_DefaultArgument,
+    CIK_DefaultInitializer
+  };
+
   NamedDecl *ParseCXXInlineMethodDef(AccessSpecifier AS,
                                 AttributeList *AccessAttrs,
                                 ParsingDeclarator &D,
@@ -1082,6 +1088,8 @@ private:
   void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI);
   void ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod);
   bool ConsumeAndStoreFunctionPrologue(CachedTokens &Toks);
+  bool ConsumeAndStoreInitializer(CachedTokens &Toks, CachedInitKind CIK);
+  bool ConsumeAndStoreConditional(CachedTokens &Toks);
   bool ConsumeAndStoreUntil(tok::TokenKind T1,
                             CachedTokens &Toks,
                             bool StopAtSemi = true,
@@ -1800,6 +1808,11 @@ private:
   isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(),
                             bool *HasMissingTypename = 0);
 
+  /// Given that isCXXDeclarationSpecifier returns \c TPResult::True or
+  /// \c TPResult::Ambiguous, determine whether the decl-specifier would be
+  /// a type-specifier other than a cv-qualifier.
+  bool isCXXDeclarationSpecifierAType();
+
   /// \brief Determine whether an identifier has been tentatively declared as a
   /// non-type. Such tentative declarations should not be found to name a type
   /// during a tentative parse, but also should not be annotated as a non-type.
@@ -1812,15 +1825,18 @@ private:
   // that more tentative parsing is necessary for disambiguation.
   // They all consume tokens, so backtracking should be used after calling them.
 
-  TPResult TryParseDeclarationSpecifier(bool *HasMissingTypename = 0);
   TPResult TryParseSimpleDeclaration(bool AllowForRangeDecl);
   TPResult TryParseTypeofSpecifier();
   TPResult TryParseProtocolQualifiers();
+  TPResult TryParsePtrOperatorSeq();
+  TPResult TryParseOperatorId();
   TPResult TryParseInitDeclaratorList();
   TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier=true);
-  TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0);
+  TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0,
+                                              bool VersusTemplateArg = false);
   TPResult TryParseFunctionDeclarator();
   TPResult TryParseBracketDeclarator();
+  TPResult TryConsumeDeclarationSpecifier();
 
 public:
   TypeResult ParseTypeName(SourceRange *Range = 0,

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Sep 12 18:28:08 2013
@@ -6208,7 +6208,7 @@ public:
 
   /// \brief RAII class used to determine whether SFINAE has
   /// trapped any errors that occur during template argument
-  /// deduction.`
+  /// deduction.
   class SFINAETrap {
     Sema &SemaRef;
     unsigned PrevSFINAEErrors;
@@ -6240,10 +6240,34 @@ public:
     }
   };
 
+  /// \brief RAII class used to indicate that we are performing provisional
+  /// semantic analysis to determine the validity of a construct, so
+  /// typo-correction and diagnostics in the immediate context (not within
+  /// implicitly-instantiated templates) should be suppressed.
+  class TentativeAnalysisScope {
+    Sema &SemaRef;
+    // FIXME: Using a SFINAETrap for this is a hack.
+    SFINAETrap Trap;
+    bool PrevDisableTypoCorrection;
+  public:
+    explicit TentativeAnalysisScope(Sema &SemaRef)
+        : SemaRef(SemaRef), Trap(SemaRef, true),
+          PrevDisableTypoCorrection(SemaRef.DisableTypoCorrection) {
+      SemaRef.DisableTypoCorrection = true;
+    }
+    ~TentativeAnalysisScope() {
+      SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection;
+    }
+  };
+
   /// \brief The current instantiation scope used to store local
   /// variables.
   LocalInstantiationScope *CurrentInstantiationScope;
 
+  /// \brief Tracks whether we are in a context where typo correction is
+  /// disabled.
+  bool DisableTypoCorrection;
+
   /// \brief The number of typos corrected by CorrectTypo.
   unsigned TyposCorrected;
 

Modified: cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp (original)
+++ cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp Thu Sep 12 18:28:08 2013
@@ -215,8 +215,7 @@ void Parser::ParseCXXNonStaticMemberInit
     ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/true);
   } else {
     // Consume everything up to (but excluding) the comma or semicolon.
-    ConsumeAndStoreUntil(tok::comma, Toks, /*StopAtSemi=*/true,
-                         /*ConsumeFinalToken=*/false);
+    ConsumeAndStoreInitializer(Toks, CIK_DefaultInitializer);
   }
 
   // Store an artificial EOF token to ensure that we don't run off the end of
@@ -345,8 +344,15 @@ void Parser::ParseLexedMethodDeclaration
       else {
         if (Tok.is(tok::cxx_defaultarg_end))
           ConsumeToken();
-        else
-          Diag(Tok.getLocation(), diag::err_default_arg_unparsed);
+        else {
+          // The last two tokens are the terminator and the saved value of
+          // Tok; the last token in the default argument is the one before
+          // those.
+          assert(Toks->size() >= 3 && "expected a token in default arg");
+          Diag(Tok.getLocation(), diag::err_default_arg_unparsed)
+            << SourceRange(Tok.getLocation(),
+                           (*Toks)[Toks->size() - 3].getLocation());
+        }
         Actions.ActOnParamDefaultArgument(LM.DefaultArgs[I].Param, EqualLoc,
                                           DefArgResult.take());
       }
@@ -814,3 +820,276 @@ bool Parser::ConsumeAndStoreFunctionProl
     }
   }
 }
+
+/// \brief Consume and store tokens from the '?' to the ':' in a conditional
+/// expression.
+bool Parser::ConsumeAndStoreConditional(CachedTokens &Toks) {
+  // Consume '?'.
+  assert(Tok.is(tok::question));
+  Toks.push_back(Tok);
+  ConsumeToken();
+
+  while (Tok.isNot(tok::colon)) {
+    if (!ConsumeAndStoreUntil(tok::question, tok::colon, Toks, /*StopAtSemi*/true,
+                              /*ConsumeFinalToken*/false))
+      return false;
+
+    // If we found a nested conditional, consume it.
+    if (Tok.is(tok::question) && !ConsumeAndStoreConditional(Toks))
+      return false;
+  }
+
+  // Consume ':'.
+  Toks.push_back(Tok);
+  ConsumeToken();
+  return true;
+}
+
+/// \brief A tentative parsing action that can also revert token annotations.
+class Parser::UnannotatedTentativeParsingAction : public TentativeParsingAction {
+public:
+  explicit UnannotatedTentativeParsingAction(Parser &Self,
+                                             tok::TokenKind EndKind)
+      : TentativeParsingAction(Self), Self(Self), EndKind(EndKind) {
+    // Stash away the old token stream, so we can restore it once the
+    // tentative parse is complete.
+    TentativeParsingAction Inner(Self);
+    Self.ConsumeAndStoreUntil(EndKind, Toks, true, /*ConsumeFinalToken*/false);
+    Inner.Revert();
+  }
+
+  void RevertAnnotations() {
+    Revert();
+
+    // Put back the original tokens.
+    Self.SkipUntil(EndKind, true, /*DontConsume*/true);
+    if (Toks.size()) {
+      Token *Buffer = new Token[Toks.size()];
+      std::copy(Toks.begin() + 1, Toks.end(), Buffer);
+      Buffer[Toks.size() - 1] = Self.Tok;
+      Self.PP.EnterTokenStream(Buffer, Toks.size(), true, /*Owned*/true);
+
+      Self.Tok = Toks.front();
+    }
+  }
+
+private:
+  Parser &Self;
+  CachedTokens Toks;
+  tok::TokenKind EndKind;
+};
+
+/// ConsumeAndStoreInitializer - Consume and store the token at the passed token
+/// container until the end of the current initializer expression (either a
+/// default argument or an in-class initializer for a non-static data member).
+/// The final token is not consumed.
+bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks,
+                                        CachedInitKind CIK) {
+  // We always want this function to consume at least one token if not at EOF.
+  bool IsFirstTokenConsumed = true;
+
+  // Number of possible unclosed <s we've seen so far. These might be templates,
+  // and might not, but if there were none of them (or we know for sure that
+  // we're within a template), we can avoid a tentative parse.
+  unsigned AngleCount = 0;
+  unsigned KnownTemplateCount = 0;
+
+  while (1) {
+    switch (Tok.getKind()) {
+    case tok::comma:
+      // If we might be in a template, perform a tentative parse to check.
+      if (!AngleCount)
+        // Not a template argument: this is the end of the initializer.
+        return true;
+      if (KnownTemplateCount)
+        goto consume_token;
+
+      // We hit a comma inside angle brackets. This is the hard case. The
+      // rule we follow is:
+      //  * For a default argument, if the tokens after the comma form a
+      //    syntactically-valid parameter-declaration-clause, in which each
+      //    parameter has an initializer, then this comma ends the default
+      //    argument.
+      //  * For a default initializer, if the tokens after the comma form a
+      //    syntactically-valid init-declarator-list, then this comma ends
+      //    the default initializer.
+      {
+        UnannotatedTentativeParsingAction PA(*this,
+                                             CIK == CIK_DefaultInitializer
+                                               ? tok::semi : tok::r_paren);
+        Sema::TentativeAnalysisScope Scope(Actions);
+
+        TPResult Result = TPResult::Error();
+        ConsumeToken();
+        switch (CIK) {
+        case CIK_DefaultInitializer:
+          Result = TryParseInitDeclaratorList();
+          // If we parsed a complete, ambiguous init-declarator-list, this
+          // is only syntactically-valid if it's followed by a semicolon.
+          if (Result == TPResult::Ambiguous() && Tok.isNot(tok::semi))
+            Result = TPResult::False();
+          break;
+
+        case CIK_DefaultArgument:
+          bool InvalidAsDeclaration = false;
+          Result = TryParseParameterDeclarationClause(
+              &InvalidAsDeclaration, /*VersusTemplateArgument*/true);
+          // If this is an expression or a declaration with a missing
+          // 'typename', assume it's not a declaration.
+          if (Result == TPResult::Ambiguous() && InvalidAsDeclaration)
+            Result = TPResult::False();
+          break;
+        }
+
+        // If what follows could be a declaration, it is a declaration.
+        if (Result != TPResult::False() && Result != TPResult::Error()) {
+          PA.Revert();
+          return true;
+        }
+
+        // In the uncommon case that we decide the following tokens are part
+        // of a template argument, revert any annotations we've performed in
+        // those tokens. We're not going to look them up until we've parsed
+        // the rest of the class, and that might add more declarations.
+        PA.RevertAnnotations();
+      }
+
+      // Keep going. We know we're inside a template argument list now.
+      ++KnownTemplateCount;
+      goto consume_token;
+
+    case tok::eof:
+      // Ran out of tokens.
+      return false;
+
+    case tok::less:
+      // FIXME: A '<' can only start a template-id if it's preceded by an
+      // identifier, an operator-function-id, or a literal-operator-id.
+      ++AngleCount;
+      goto consume_token;
+
+    case tok::question:
+      // In 'a ? b : c', 'b' can contain an unparenthesized comma. If it does,
+      // that is *never* the end of the initializer. Skip to the ':'.
+      if (!ConsumeAndStoreConditional(Toks))
+        return false;
+      break;
+
+    case tok::greatergreatergreater:
+      if (!getLangOpts().CPlusPlus11)
+        goto consume_token;
+      if (AngleCount) --AngleCount;
+      if (KnownTemplateCount) --KnownTemplateCount;
+      // Fall through.
+    case tok::greatergreater:
+      if (!getLangOpts().CPlusPlus11)
+        goto consume_token;
+      if (AngleCount) --AngleCount;
+      if (KnownTemplateCount) --KnownTemplateCount;
+      // Fall through.
+    case tok::greater:
+      if (AngleCount) --AngleCount;
+      if (KnownTemplateCount) --KnownTemplateCount;
+      goto consume_token;
+
+    case tok::kw_template:
+      // 'template' identifier '<' is known to start a template argument list,
+      // and can be used to disambiguate the parse.
+      // FIXME: Support all forms of 'template' unqualified-id '<'.
+      Toks.push_back(Tok);
+      ConsumeToken();
+      if (Tok.is(tok::identifier)) {
+        Toks.push_back(Tok);
+        ConsumeToken();
+        if (Tok.is(tok::less)) {
+          ++KnownTemplateCount;
+          Toks.push_back(Tok);
+          ConsumeToken();
+        }
+      }
+      break;
+
+    case tok::kw_operator:
+      // If 'operator' precedes other punctuation, that punctuation loses
+      // its special behavior.
+      Toks.push_back(Tok);
+      ConsumeToken();
+      switch (Tok.getKind()) {
+      case tok::comma:
+      case tok::greatergreatergreater:
+      case tok::greatergreater:
+      case tok::greater:
+      case tok::less:
+        Toks.push_back(Tok);
+        ConsumeToken();
+        break;
+      default:
+        break;
+      }
+      break;
+
+    case tok::l_paren:
+      // Recursively consume properly-nested parens.
+      Toks.push_back(Tok);
+      ConsumeParen();
+      ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+      break;
+    case tok::l_square:
+      // Recursively consume properly-nested square brackets.
+      Toks.push_back(Tok);
+      ConsumeBracket();
+      ConsumeAndStoreUntil(tok::r_square, Toks, /*StopAtSemi=*/false);
+      break;
+    case tok::l_brace:
+      // Recursively consume properly-nested braces.
+      Toks.push_back(Tok);
+      ConsumeBrace();
+      ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+      break;
+
+    // Okay, we found a ']' or '}' or ')', which we think should be balanced.
+    // Since the user wasn't looking for this token (if they were, it would
+    // already be handled), this isn't balanced.  If there is a LHS token at a
+    // higher level, we will assume that this matches the unbalanced token
+    // and return it.  Otherwise, this is a spurious RHS token, which we skip.
+    case tok::r_paren:
+      if (CIK == CIK_DefaultArgument)
+        return true; // End of the default argument.
+      if (ParenCount && !IsFirstTokenConsumed)
+        return false;  // Matches something.
+      goto consume_token;
+    case tok::r_square:
+      if (BracketCount && !IsFirstTokenConsumed)
+        return false;  // Matches something.
+      goto consume_token;
+    case tok::r_brace:
+      if (BraceCount && !IsFirstTokenConsumed)
+        return false;  // Matches something.
+      goto consume_token;
+
+    case tok::code_completion:
+      Toks.push_back(Tok);
+      ConsumeCodeCompletionToken();
+      break;
+
+    case tok::string_literal:
+    case tok::wide_string_literal:
+    case tok::utf8_string_literal:
+    case tok::utf16_string_literal:
+    case tok::utf32_string_literal:
+      Toks.push_back(Tok);
+      ConsumeStringToken();
+      break;
+    case tok::semi:
+      if (CIK == CIK_DefaultInitializer)
+        return true; // End of the default initializer.
+      // FALL THROUGH.
+    default:
+    consume_token:
+      Toks.push_back(Tok);
+      ConsumeToken();
+      break;
+    }
+    IsFirstTokenConsumed = false;
+  }
+}

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Thu Sep 12 18:28:08 2013
@@ -5333,9 +5333,7 @@ void Parser::ParseParameterDeclarationCl
           // FIXME: Can we use a smart pointer for Toks?
           DefArgToks = new CachedTokens;
 
-          if (!ConsumeAndStoreUntil(tok::comma, tok::r_paren, *DefArgToks,
-                                    /*StopAtSemi=*/true,
-                                    /*ConsumeFinalToken=*/false)) {
+          if (!ConsumeAndStoreInitializer(*DefArgToks, CIK_DefaultArgument)) {
             delete DefArgToks;
             DefArgToks = 0;
             Actions.ActOnParamDefaultArgumentError(Param);

Modified: cfe/trunk/lib/Parse/ParseTentative.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTentative.cpp?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseTentative.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTentative.cpp Thu Sep 12 18:28:08 2013
@@ -142,6 +142,82 @@ bool Parser::isCXXSimpleDeclaration(bool
   return TPR == TPResult::True();
 }
 
+/// Try to consume a token sequence that we've already identified as
+/// (potentially) starting a decl-specifier.
+Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
+  switch (Tok.getKind()) {
+  case tok::kw__Atomic:
+    if (NextToken().isNot(tok::l_paren)) {
+      ConsumeToken();
+      break;
+    }
+    // Fall through.
+  case tok::kw_typeof:
+  case tok::kw___attribute:
+  case tok::kw___underlying_type: {
+    ConsumeToken();
+    if (Tok.isNot(tok::l_paren))
+      return TPResult::Error();
+    ConsumeParen();
+    if (!SkipUntil(tok::r_paren, false))
+      return TPResult::Error();
+    break;
+  }
+
+  case tok::kw_class:
+  case tok::kw_struct:
+  case tok::kw_union:
+  case tok::kw___interface:
+  case tok::kw_enum:
+    // elaborated-type-specifier:
+    //     class-key attribute-specifier-seq[opt]
+    //         nested-name-specifier[opt] identifier
+    //     class-key nested-name-specifier[opt] template[opt] simple-template-id
+    //     enum nested-name-specifier[opt] identifier
+    //
+    // FIXME: We don't support class-specifiers nor enum-specifiers here.
+    ConsumeToken();
+
+    // Skip attributes.
+    while (Tok.is(tok::l_square) || Tok.is(tok::kw___attribute) ||
+           Tok.is(tok::kw___declspec) || Tok.is(tok::kw_alignas)) {
+      if (Tok.is(tok::l_square)) {
+        ConsumeBracket();
+        if (!SkipUntil(tok::r_square, false))
+          return TPResult::Error();
+      } else {
+        ConsumeToken();
+        if (Tok.isNot(tok::l_paren))
+          return TPResult::Error();
+        ConsumeParen();
+        if (!SkipUntil(tok::r_paren, false))
+          return TPResult::Error();
+      }
+    }
+
+    if (TryAnnotateCXXScopeToken())
+      return TPResult::Error();
+    if (Tok.is(tok::annot_cxxscope))
+      ConsumeToken();
+    if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
+      return TPResult::Error();
+    ConsumeToken();
+    break;
+
+  case tok::annot_cxxscope:
+    ConsumeToken();
+    // Fall through.
+  default:
+    ConsumeToken();
+
+    if (getLangOpts().ObjC1 && Tok.is(tok::less))
+      return TryParseProtocolQualifiers();
+    break;
+  }
+
+  return TPResult::Ambiguous();
+}
+
 /// simple-declaration:
 ///   decl-specifier-seq init-declarator-list[opt] ';'
 ///
@@ -151,16 +227,8 @@ bool Parser::isCXXSimpleDeclaration(bool
 ///    attribute-specifier-seqopt type-specifier-seq declarator
 ///
 Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
-  if (Tok.is(tok::kw_typeof))
-    TryParseTypeofSpecifier();
-  else {
-    if (Tok.is(tok::annot_cxxscope))
-      ConsumeToken();
-    ConsumeToken();
-
-    if (getLangOpts().ObjC1 && Tok.is(tok::less))
-      TryParseProtocolQualifiers();
-  }
+  if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+    return TPResult::Error();
 
   // Two decl-specifiers in a row conclusively disambiguate this as being a
   // simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the
@@ -233,7 +301,7 @@ Parser::TPResult Parser::TryParseInitDec
       // expression can never be followed directly by a braced-init-list.
       return TPResult::True();
     } else if (Tok.is(tok::equal) || isTokIdentifier_in()) {
-      // MSVC and g++ won't examine the rest of declarators if '=' is 
+      // MSVC and g++ won't examine the rest of declarators if '=' is
       // encountered; they just conclude that we have a declaration.
       // EDG parses the initializer completely, which is the proper behavior
       // for this case.
@@ -241,12 +309,14 @@ Parser::TPResult Parser::TryParseInitDec
       // At present, Clang follows MSVC and g++, since the parser does not have
       // the ability to parse an expression fully without recording the
       // results of that parse.
-      // Also allow 'in' after on objective-c declaration as in: 
-      // for (int (^b)(void) in array). Ideally this should be done in the 
+      // FIXME: Handle this case correctly.
+      //
+      // Also allow 'in' after an Objective-C declaration as in:
+      // for (int (^b)(void) in array). Ideally this should be done in the
       // context of parsing for-init-statement of a foreach statement only. But,
       // in any other context 'in' is invalid after a declaration and parser
       // issues the error regardless of outcome of this decision.
-      // FIXME. Change if above assumption does not hold.
+      // FIXME: Change if above assumption does not hold.
       return TPResult::True();
     }
 
@@ -286,14 +356,7 @@ bool Parser::isCXXConditionDeclaration()
   TentativeParsingAction PA(*this);
 
   // type-specifier-seq
-  if (Tok.is(tok::kw_typeof))
-    TryParseTypeofSpecifier();
-  else {
-    ConsumeToken();
-    
-    if (getLangOpts().ObjC1 && Tok.is(tok::less))
-      TryParseProtocolQualifiers();
-  }
+  TryConsumeDeclarationSpecifier();
   assert(Tok.is(tok::l_paren) && "Expected '('");
 
   // declarator
@@ -363,15 +426,7 @@ bool Parser::isCXXTypeId(TentativeCXXTyp
   TentativeParsingAction PA(*this);
 
   // type-specifier-seq
-  if (Tok.is(tok::kw_typeof))
-    TryParseTypeofSpecifier();
-  else {
-    ConsumeToken();
-    
-    if (getLangOpts().ObjC1 && Tok.is(tok::less))
-      TryParseProtocolQualifiers();
-  }
-  
+  TryConsumeDeclarationSpecifier();
   assert(Tok.is(tok::l_paren) && "Expected '('");
 
   // declarator
@@ -569,6 +624,121 @@ Parser::isCXX11AttributeSpecifier(bool D
   return CAK_NotAttributeSpecifier;
 }
 
+Parser::TPResult Parser::TryParsePtrOperatorSeq() {
+  while (true) {
+    if (Tok.is(tok::coloncolon) || Tok.is(tok::identifier))
+      if (TryAnnotateCXXScopeToken(true))
+        return TPResult::Error();
+
+    if (Tok.is(tok::star) || Tok.is(tok::amp) || Tok.is(tok::caret) ||
+        Tok.is(tok::ampamp) ||
+        (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
+      // ptr-operator
+      ConsumeToken();
+      while (Tok.is(tok::kw_const)    ||
+             Tok.is(tok::kw_volatile) ||
+             Tok.is(tok::kw_restrict))
+        ConsumeToken();
+    } else {
+      return TPResult::True();
+    }
+  }
+}
+
+///         operator-function-id:
+///           'operator' operator
+///
+///         operator: one of
+///           new  delete  new[]  delete[]  +  -  *  /  %  ^  [...]
+///
+///         conversion-function-id:
+///           'operator' conversion-type-id
+///
+///         conversion-type-id:
+///           type-specifier-seq conversion-declarator[opt]
+///
+///         conversion-declarator:
+///           ptr-operator conversion-declarator[opt]
+///
+///         literal-operator-id:
+///           'operator' string-literal identifier
+///           'operator' user-defined-string-literal
+Parser::TPResult Parser::TryParseOperatorId() {
+  assert(Tok.is(tok::kw_operator));
+  ConsumeToken();
+
+  // Maybe this is an operator-function-id.
+  switch (Tok.getKind()) {
+  case tok::kw_new: case tok::kw_delete:
+    ConsumeToken();
+    if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) {
+      ConsumeBracket();
+      ConsumeBracket();
+    }
+    return TPResult::True();
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemOnly) \
+  case tok::Token:
+#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemOnly)
+#include "clang/Basic/OperatorKinds.def"
+    ConsumeToken();
+    return TPResult::True();
+
+  case tok::l_square:
+    if (NextToken().is(tok::r_square)) {
+      ConsumeBracket();
+      ConsumeBracket();
+      return TPResult::True();
+    }
+    break;
+
+  case tok::l_paren:
+    if (NextToken().is(tok::r_paren)) {
+      ConsumeParen();
+      ConsumeParen();
+      return TPResult::True();
+    }
+    break;
+
+  default:
+    break;
+  }
+
+  // Maybe this is a literal-operator-id.
+  if (getLangOpts().CPlusPlus11 && isTokenStringLiteral()) {
+    bool FoundUDSuffix = false;
+    do {
+      FoundUDSuffix |= Tok.hasUDSuffix();
+      ConsumeStringToken();
+    } while (isTokenStringLiteral());
+
+    if (!FoundUDSuffix) {
+      if (Tok.is(tok::identifier))
+        ConsumeToken();
+      else
+        return TPResult::Error();
+    }
+    return TPResult::True();
+  }
+
+  // Maybe this is a conversion-function-id.
+  bool AnyDeclSpecifiers = false;
+  while (true) {
+    TPResult TPR = isCXXDeclarationSpecifier();
+    if (TPR == TPResult::Error())
+      return TPR;
+    if (TPR == TPResult::False()) {
+      if (!AnyDeclSpecifiers)
+        return TPResult::Error();
+      break;
+    }
+    if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+      return TPResult::Error();
+    AnyDeclSpecifiers = true;
+  }
+  return TryParsePtrOperatorSeq();
+}
+
 ///         declarator:
 ///           direct-declarator
 ///           ptr-operator declarator
@@ -615,9 +785,11 @@ Parser::isCXX11AttributeSpecifier(bool D
 ///
 ///         unqualified-id:
 ///           identifier
-///           operator-function-id                                        [TODO]
-///           conversion-function-id                                      [TODO]
+///           operator-function-id
+///           conversion-function-id
+///           literal-operator-id
 ///           '~' class-name                                              [TODO]
+///           '~' decltype-specifier                                      [TODO]
 ///           template-id                                                 [TODO]
 ///
 Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
@@ -625,40 +797,28 @@ Parser::TPResult Parser::TryParseDeclara
   // declarator:
   //   direct-declarator
   //   ptr-operator declarator
-
-  while (1) {
-    if (Tok.is(tok::coloncolon) || Tok.is(tok::identifier))
-      if (TryAnnotateCXXScopeToken(true))
-        return TPResult::Error();
-
-    if (Tok.is(tok::star) || Tok.is(tok::amp) || Tok.is(tok::caret) ||
-        Tok.is(tok::ampamp) ||
-        (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
-      // ptr-operator
-      ConsumeToken();
-      while (Tok.is(tok::kw_const)    ||
-             Tok.is(tok::kw_volatile) ||
-             Tok.is(tok::kw_restrict))
-        ConsumeToken();
-    } else {
-      break;
-    }
-  }
+  if (TryParsePtrOperatorSeq() == TPResult::Error())
+    return TPResult::Error();
 
   // direct-declarator:
   // direct-abstract-declarator:
   if (Tok.is(tok::ellipsis))
     ConsumeToken();
-  
-  if ((Tok.is(tok::identifier) ||
-       (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier))) &&
+
+  if ((Tok.is(tok::identifier) || Tok.is(tok::kw_operator) ||
+       (Tok.is(tok::annot_cxxscope) && (NextToken().is(tok::identifier) ||
+                                        NextToken().is(tok::kw_operator)))) &&
       mayHaveIdentifier) {
     // declarator-id
     if (Tok.is(tok::annot_cxxscope))
       ConsumeToken();
-    else
+    else if (Tok.is(tok::identifier))
       TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo());
-    ConsumeToken();
+    if (Tok.is(tok::kw_operator)) {
+      if (TryParseOperatorId() == TPResult::Error())
+        return TPResult::Error();
+    } else
+      ConsumeToken();
   } else if (Tok.is(tok::l_paren)) {
     ConsumeParen();
     if (mayBeAbstract &&
@@ -836,14 +996,15 @@ Parser::isExpressionOrTypeSpecifierSimpl
   case tok::kw_wchar_t:
   case tok::kw_char16_t:
   case tok::kw_char32_t:
-  case tok::kw___underlying_type:
   case tok::kw__Decimal32:
   case tok::kw__Decimal64:
   case tok::kw__Decimal128:
+  case tok::kw___interface:
   case tok::kw___thread:
   case tok::kw_thread_local:
   case tok::kw__Thread_local:
   case tok::kw_typeof:
+  case tok::kw___underlying_type:
   case tok::kw___cdecl:
   case tok::kw___stdcall:
   case tok::kw___fastcall:
@@ -1103,6 +1264,7 @@ Parser::isCXXDeclarationSpecifier(Parser
   case tok::kw_class:
   case tok::kw_struct:
   case tok::kw_union:
+  case tok::kw___interface:
     // enum-specifier
   case tok::kw_enum:
     // cv-qualifier
@@ -1325,6 +1487,56 @@ Parser::isCXXDeclarationSpecifier(Parser
   }
 }
 
+bool Parser::isCXXDeclarationSpecifierAType() {
+  switch (Tok.getKind()) {
+    // typename-specifier
+  case tok::annot_decltype:
+  case tok::annot_template_id:
+  case tok::annot_typename:
+  case tok::kw_typeof:
+  case tok::kw___underlying_type:
+    return true;
+
+    // elaborated-type-specifier
+  case tok::kw_class:
+  case tok::kw_struct:
+  case tok::kw_union:
+  case tok::kw___interface:
+  case tok::kw_enum:
+    return true;
+
+    // simple-type-specifier
+  case tok::kw_char:
+  case tok::kw_wchar_t:
+  case tok::kw_char16_t:
+  case tok::kw_char32_t:
+  case tok::kw_bool:
+  case tok::kw_short:
+  case tok::kw_int:
+  case tok::kw_long:
+  case tok::kw___int64:
+  case tok::kw___int128:
+  case tok::kw_signed:
+  case tok::kw_unsigned:
+  case tok::kw_half:
+  case tok::kw_float:
+  case tok::kw_double:
+  case tok::kw_void:
+  case tok::kw___unknown_anytype:
+    return true;
+
+  case tok::kw_auto:
+    return getLangOpts().CPlusPlus11;
+
+  case tok::kw__Atomic:
+    // "_Atomic foo"
+    return NextToken().is(tok::l_paren);
+
+  default:
+    return false;
+  }
+}
+
 /// [GNU] typeof-specifier:
 ///         'typeof' '(' expressions ')'
 ///         'typeof' '(' type-name ')'
@@ -1366,27 +1578,6 @@ Parser::TPResult Parser::TryParseProtoco
   return TPResult::Error();
 }
 
-Parser::TPResult
-Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) {
-  TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(),
-                                           HasMissingTypename);
-  if (TPR != TPResult::Ambiguous())
-    return TPR;
-
-  if (Tok.is(tok::kw_typeof))
-    TryParseTypeofSpecifier();
-  else {
-    if (Tok.is(tok::annot_cxxscope))
-      ConsumeToken();
-    ConsumeToken();
-    
-    if (getLangOpts().ObjC1 && Tok.is(tok::less))
-      TryParseProtocolQualifiers();
-  }
-
-  return TPResult::Ambiguous();
-}
-
 /// isCXXFunctionDeclarator - Disambiguates between a function declarator or
 /// a constructor-style initializer, when parsing declaration statements.
 /// Returns true for function declarator and false for constructor-style
@@ -1461,7 +1652,8 @@ bool Parser::isCXXFunctionDeclarator(boo
 ///     attributes[opt] '=' assignment-expression
 ///
 Parser::TPResult
-Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) {
+Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
+                                           bool VersusTemplateArgument) {
 
   if (Tok.is(tok::r_paren))
     return TPResult::Ambiguous();
@@ -1494,8 +1686,32 @@ Parser::TryParseParameterDeclarationClau
     // decl-specifier-seq
     // A parameter-declaration's initializer must be preceded by an '=', so
     // decl-specifier-seq '{' is not a parameter in C++11.
-    TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration);
-    if (TPR != TPResult::Ambiguous())
+    TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(),
+                                             InvalidAsDeclaration);
+
+    if (VersusTemplateArgument && TPR == TPResult::True()) {
+      // Consume the decl-specifier-seq. We have to look past it, since a
+      // type-id might appear here in a template argument.
+      bool SeenType = false;
+      do {
+        SeenType |= isCXXDeclarationSpecifierAType();
+        if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+          return TPResult::Error();
+
+        // If we see a parameter name, this can't be a template argument.
+        if (SeenType && Tok.is(tok::identifier))
+          return TPResult::True();
+
+        TPR = isCXXDeclarationSpecifier(TPResult::False(),
+                                        InvalidAsDeclaration);
+        if (TPR == TPResult::Error())
+          return TPR;
+      } while (TPR != TPResult::False());
+    } else if (TPR == TPResult::Ambiguous()) {
+      // Disambiguate what follows the decl-specifier.
+      if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+        return TPResult::Error();
+    } else
       return TPR;
 
     // declarator
@@ -1508,9 +1724,24 @@ Parser::TryParseParameterDeclarationClau
     if (Tok.is(tok::kw___attribute))
       return TPResult::True();
 
+    // If we're disambiguating a template argument in a default argument in
+    // a class definition versus a parameter declaration, an '=' here
+    // disambiguates the parse one way or the other.
+    // If this is a parameter, it must have a default argument because
+    //   (a) the previous parameter did, and
+    //   (b) this must be the first declaration of the function, so we can't
+    //       inherit any default arguments from elsewhere.
+    // If we see an ')', then we've reached the end of a
+    // parameter-declaration-clause, and the last param is missing its default
+    // argument.
+    if (VersusTemplateArgument)
+      return (Tok.is(tok::equal) || Tok.is(tok::r_paren)) ? TPResult::True()
+                                                          : TPResult::False();
+
     if (Tok.is(tok::equal)) {
       // '=' assignment-expression
       // Parse through assignment-expression.
+      // FIXME: assignment-expression may contain an unparenthesized comma.
       if (!SkipUntil(tok::comma, tok::r_paren, true/*StopAtSemi*/,
                      true/*DontConsume*/))
         return TPResult::Error();

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Thu Sep 12 18:28:08 2013
@@ -89,8 +89,9 @@ Sema::Sema(Preprocessor &pp, ASTContext
     NumSFINAEErrors(0), InFunctionDeclarator(0),
     AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
     NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
-    CurrentInstantiationScope(0), TyposCorrected(0),
-    AnalysisWarnings(*this), VarDataSharingAttributesStack(0), CurScope(0),
+    CurrentInstantiationScope(0), DisableTypoCorrection(false),
+    TyposCorrected(0), AnalysisWarnings(*this),
+    VarDataSharingAttributesStack(0), CurScope(0),
     Ident_super(0), Ident___float128(0)
 {
   TUScope = 0;

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Thu Sep 12 18:28:08 2013
@@ -3955,7 +3955,8 @@ TypoCorrection Sema::CorrectTypo(const D
       return Correction;
   }
 
-  if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking)
+  if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking ||
+      DisableTypoCorrection)
     return TypoCorrection();
 
   // In Microsoft mode, don't perform typo correction in a template member

Added: cfe/trunk/test/Parser/cxx-ambig-init-templ.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-ambig-init-templ.cpp?rev=190639&view=auto
==============================================================================
--- cfe/trunk/test/Parser/cxx-ambig-init-templ.cpp (added)
+++ cfe/trunk/test/Parser/cxx-ambig-init-templ.cpp Thu Sep 12 18:28:08 2013
@@ -0,0 +1,171 @@
+// RUN: %clang_cc1 -std=c++11 -verify %s
+
+template<int> struct c { c(int) = delete; typedef void val; operator int() const; };
+
+int val;
+int foobar;
+struct S {
+  int k1 = a < b < c, d > ::val, e1;
+  int k2 = a < b, c < d > ::val, e2;
+  int k3 = b < a < c, d > ::val, e3;
+  int k4 = b < c, x, y = d > ::val, e4;
+  int k5 = T1 < b, &S::operator=(int); // expected-error {{extra qualification}}
+  int k6 = T2 < b, &S::operator= >::val;
+  int k7 = T1 < b, &S::operator>(int); // expected-error {{extra qualification}}
+  int k8 = T2 < b, &S::operator> >::val;
+  int k9 = T3 < a < b, c >> (d), e5 = 1 > (e4);
+  int k10 = 0 < T3 < a < b, c >> (d
+      ) // expected-error {{expected ';' at end of declaration}}
+      , a > (e4);
+  int k11 = 0 < 1, c<3>::*ptr;
+  int k12 = e < 0, int a<b<c>::* >(), e11;
+
+  void f1(
+    int k1 = a < b < c, d > ::val,
+    int k2 = b < a < c, d > ::val,
+    int k3 = b < c, int x = 0 > ::val,
+    int k4 = a < b, T3 < int > >(), // expected-error {{must be an expression}}
+    int k5 = a < b, c < d > ::val,
+    int k6 = a < b, c < d > (n) // expected-error {{undeclared identifier 'n'}}
+  );
+
+  void f2a(
+    // T3<int> here is a parameter type, so must be declared before it is used.
+    int k1 = c < b, T3 < int > x = 0 // expected-error {{unexpected end of default argument expression}}
+  );
+
+  template<typename, int=0> struct T3 { T3(int); operator int(); };
+
+  void f2b(
+    int k1 = c < b, T3 < int > x  = 0 // ok
+  );
+
+  // This is a one-parameter function. Ensure we don't typo-correct it to
+  //     int = a < b, c < foobar > ()
+  // ... which would be a function with two parameters.
+  int f3(int = a < b, c < goobar > ());
+  static constexpr int (S::*f3_test)(int) = &S::f3;
+
+  void f4(
+    int k1 = a<1,2>::val,
+    int missing_default // expected-error {{missing default argument on parameter}}
+  );
+
+  void f5(
+    int k1 = b < c,
+    int missing_default // expected-error {{missing default argument on parameter}}
+  );
+
+  void f6(
+    int k = b < c,
+    unsigned int (missing_default) // expected-error {{missing default argument on parameter}}
+  );
+
+  template<int, int=0> struct a { static const int val = 0; operator int(); }; // expected-note {{here}}
+  static const int b = 0, c = 1, d = 2, goobar = 3;
+  template<int, typename> struct e { operator int(); };
+
+  int mp1 = 0 < 1,
+      a<b<c,b<c>::*mp2,
+      mp3 = 0 > a<b<c>::val,
+      a<b<c,b<c>::*mp4 = 0,
+      a<b<c,b<c>::*mp5 {0},
+      a<b<c,b<c>::*mp6;
+
+  int np1 = e<0, int a<b<c,b<c>::*>();
+
+  static const int T1 = 4;
+  template<int, int &(S::*)(int)> struct T2 { static const int val = 0; };
+};
+
+namespace NoAnnotationTokens {
+  template<bool> struct Bool { Bool(int); };
+  static const bool in_class = false;
+
+  struct Test {
+    // Check we don't keep around a Bool<false> annotation token here.
+    int f(Bool<true> = X<Y, Bool<in_class> >(0));
+
+    // But it's OK if we do here.
+    int g(Bool<true> = Z<Y, Bool<in_class> = Bool<false>(0));
+
+    static const bool in_class = true;
+    template<int, typename U> using X = U;
+    static const int Y = 0, Z = 0;
+  };
+}
+
+namespace ImplicitInstantiation {
+  template<typename T> struct HasError { typename T::error error; }; // expected-error {{has no members}}
+
+  struct S {
+    // This triggers the instantiation of the outer HasError<int> during
+    // disambiguation, even though it uses the inner HasError<int>.
+    void f(int a = X<Y, HasError<int>::Z >()); // expected-note {{in instantiation of}}
+
+    template<typename, typename> struct X { operator int(); };
+    typedef int Y;
+    template<typename> struct HasError { typedef int Z; };
+  };
+
+  HasError<int> hei;
+}
+
+namespace CWG325 {
+  template <int A, typename B> struct T { static int i; operator int(); };
+  class C {
+    int Foo (int i = T<1, int>::i);
+  };
+
+  class D {
+    int Foo (int i = T<1, int>::i);
+    template <int A, typename B> struct T {static int i;};
+  };
+
+  const int a = 0;
+  typedef int b;
+  T<a,b> c;
+  struct E {
+    int n = T<a,b>(c);
+  };
+}
+
+namespace Operators {
+  struct Y {};
+  constexpr int operator,(const Y&, const Y&) { return 8; }
+  constexpr int operator>(const Y&, const Y&) { return 8; }
+  constexpr int operator<(const Y&, const Y&) { return 8; }
+  constexpr int operator>>(const Y&, const Y&) { return 8; }
+
+  struct X {
+    typedef int (*Fn)(const Y&, const Y&);
+
+    Fn a = operator,, b = operator<, c = operator>;
+    void f(Fn a = operator,, Fn b = operator<, Fn c = operator>);
+
+    int k1 = T1<0, operator<, operator>, operator<>::val, l1;
+    int k2 = T1<0, operator>, operator,, operator,>::val, l2;
+    int k3 = T2<0, operator,(Y{}, Y{}),  operator<(Y{}, Y{})>::val, l3;
+    int k4 = T2<0, operator>(Y{}, Y{}),  operator,(Y{}, Y{})>::val, l4;
+    int k5 = T3<0, operator>>>::val, l5;
+    int k6 = T4<0, T3<0, operator>>>>::val, l6;
+
+    template<int, Fn, Fn, Fn> struct T1 { enum { val }; };
+    template<int, int, int> struct T2 { enum { val }; };
+    template<int, Fn> struct T3 { enum { val }; };
+    template<int, typename T> struct T4 : T {};
+  };
+}
+
+namespace ElaboratedTypeSpecifiers {
+  struct S {
+    int f(int x = T<a, struct S>());
+    int g(int x = T<a, class __declspec() C>());
+    int h(int x = T<a, union __attribute__(()) U>());
+    int i(int x = T<a, enum E>());
+    int j(int x = T<a, struct S::template T<0, enum E>>());
+    template <int, typename> struct T { operator int(); };
+    static const int a = 0;
+    enum E {};
+  };
+}

Modified: cfe/trunk/test/Parser/cxx-default-args.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-default-args.cpp?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-default-args.cpp (original)
+++ cfe/trunk/test/Parser/cxx-default-args.cpp Thu Sep 12 18:28:08 2013
@@ -14,3 +14,20 @@ typedef struct Inst {
 struct X {
   void f(int x = 1:); // expected-error {{unexpected end of default argument expression}}
 };
+
+// PR13657
+struct T {
+  template <typename A, typename B> struct T1 { enum {V};};
+  template <int A, int B> struct T2 { enum {V}; };
+  template <int, int> static int func(int);
+
+
+  void f1(T1<int, int> = T1<int, int>());
+  void f2(T1<int, double> = T1<int, double>(), T2<0, 5> = T2<0, 5>());
+  void f3(int a = T2<0, (T1<int, int>::V > 10) ? 5 : 6>::V, bool b = 4<5 );
+  void f4(bool a = 1 < 0, bool b = 2 > 0 );
+  void f5(bool a = 1 > T2<0, 0>::V, bool b = T1<int,int>::V < 3, int c = 0);
+  void f6(bool a = T2<0,3>::V < 4, bool b = 4 > T2<0,3>::V);
+  void f7(bool a = T1<int, bool>::V < 3);
+  void f8(int = func<0,1<2>(0), int = 1<0, T1<int,int>(int) = 0);
+};

Modified: cfe/trunk/test/Parser/cxx0x-member-initializers.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx0x-member-initializers.cpp?rev=190639&r1=190638&r2=190639&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx0x-member-initializers.cpp (original)
+++ cfe/trunk/test/Parser/cxx0x-member-initializers.cpp Thu Sep 12 18:28:08 2013
@@ -27,3 +27,13 @@ struct V1 {
   int a, b;
   V1() : a(), b{} {}
 };
+
+template <typename, typename> struct T1 { enum {V};};
+template <int, int> struct T2 { enum {V};};
+struct A {
+  T1<int, int> a1 = T1<int, int>(), *a2 = new T1<int,int>;
+  T2<0,0> b1 = T2<0,0>(), b2 = T2<0,0>(), b3;
+  bool c1 = 1 < 2, c2 = 2 < 1, c3 = false;
+  bool d1 = T1<int, T1<int, int>>::V < 3, d2;
+  T1<int, int()> e = T1<int, int()>();
+};





More information about the cfe-commits mailing list