]> granicus.if.org Git - clang/commitdiff
Delay parsing of default arguments of member functions until the class
authorDouglas Gregor <dgregor@apple.com>
Tue, 16 Dec 2008 21:30:33 +0000 (21:30 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 16 Dec 2008 21:30:33 +0000 (21:30 +0000)
is completely defined (C++ [class.mem]p2).

Reverse the order in which we process the definitions of member
functions specified inline. This way, we'll get diagnostics in the
order in which the member functions were declared in the class.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61103 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Action.h
include/clang/Parse/DeclSpec.h
include/clang/Parse/Parser.h
lib/Parse/ParseCXXInlineMethods.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
test/SemaCXX/default2.cpp

index af7255490822dfa26e5d46c844a4ba6d0e0c49e5..2bd2e4b22fbdeced06cbd13af9afed11eedf0c76 100644 (file)
@@ -216,7 +216,7 @@ public:
     return;
   }
 
-  /// ActOnFunctionDefBody - This is called when a function body has completed
+  /// ActOnFinishFunctionBody - This is called when a function body has completed
   /// parsing.  Decl is the DeclTy returned by ParseStartOfFunctionDef.
   virtual DeclTy *ActOnFinishFunctionBody(DeclTy *Decl, StmtArg Body) {
     return Decl;
@@ -681,6 +681,10 @@ public:
                                          ExprTy *defarg) {
   }
 
+  /// ActOnParamDefaultArgumentError - Parsing or semantic analysis of
+  /// the default argument for the parameter param failed.
+  virtual void ActOnParamDefaultArgumentError(DeclTy *param) { }
+
   /// AddCXXDirectInitializerToDecl - This action is called immediately after 
   /// ActOnDeclarator, when a C++ direct initializer is present.
   /// e.g: "int x(1);"
@@ -692,6 +696,34 @@ public:
     return;
   }
   
+  /// ActOnStartDelayedCXXMethodDeclaration - We have completed
+  /// parsing a top-level (non-nested) C++ class, and we are now
+  /// parsing those parts of the given Method declaration that could
+  /// not be parsed earlier (C++ [class.mem]p2), such as default
+  /// arguments. This action should enter the scope of the given
+  /// Method declaration as if we had just parsed the qualified method
+  /// name. However, it should not bring the parameters into scope;
+  /// that will be performed by ActOnDelayedCXXMethodParameter.
+  virtual void ActOnStartDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method) {
+  }
+
+  /// ActOnDelayedCXXMethodParameter - We've already started a delayed
+  /// C++ method declaration. We're (re-)introducing the given
+  /// function parameter into scope for use in parsing later parts of
+  /// the method declaration. For example, we could see an
+  /// ActOnParamDefaultArgument event for this parameter.
+  virtual void ActOnDelayedCXXMethodParameter(Scope *S, DeclTy *Param) {
+  }
+
+  /// ActOnFinishDelayedCXXMethodDeclaration - We have finished
+  /// processing the delayed method declaration for Method. The method
+  /// declaration is now considered finished. There may be a separate
+  /// ActOnStartOfFunctionDef action later (not necessarily
+  /// immediately!) for this method, if it was also defined inside the
+  /// class body.
+  virtual void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method) {
+  }
+
   //===------------------------- C++ Expressions --------------------------===//
   
   /// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.
index ab52a298fa836aa9bc5a9bb1fbb56f7ae0123707..4a97fd60ed2fccfd081ba1adf42aed963314cac2 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "clang/Parse/Action.h"
 #include "clang/Parse/AttributeList.h"
+#include "clang/Lex/Token.h"
 #include "llvm/ADT/SmallVector.h"
 
 namespace clang {
@@ -414,6 +415,10 @@ public:
   }
 };
   
+/// CachedTokens - A set of tokens that has been cached for later
+/// parsing.
+typedef llvm::SmallVector<Token, 4> CachedTokens;
+
 /// DeclaratorChunk - One instance of this struct is used for each type in a
 /// declarator that is parsed.
 ///
@@ -471,9 +476,19 @@ struct DeclaratorChunk {
     IdentifierInfo *Ident;
     SourceLocation IdentLoc;
     Action::DeclTy *Param;
+
+    /// DefaultArgTokens - When the parameter's default argument
+    /// cannot be parsed immediately (because it occurs within the
+    /// declaration of a member function), it will be stored here as a
+    /// sequence of tokens to be parsed once the class definition is
+    /// complete. Non-NULL indicates that there is a default argument.
+    CachedTokens   *DefaultArgTokens;
+
     ParamInfo() {}
-    ParamInfo(IdentifierInfo *ident, SourceLocation iloc, Action::DeclTy *param)
-      : Ident(ident), IdentLoc(iloc), Param(param) {}
+    ParamInfo(IdentifierInfo *ident, SourceLocation iloc, Action::DeclTy *param,
+              CachedTokens *DefArgTokens = 0)
+      : Ident(ident), IdentLoc(iloc), Param(param), 
+        DefaultArgTokens(DefArgTokens) {}
   };
   
   struct FunctionTypeInfo {
@@ -605,7 +620,6 @@ struct DeclaratorChunk {
   }
 };
 
-
 /// Declarator - Information about one declarator, including the parsed type
 /// information and the identifier.  When the declarator is fully formed, this
 /// is turned into the appropriate Decl object.
index ba5cc84b99c25fbcbcb0e0f9e1858996c0c1e581..579d52303c6ee15fc17630f93e3d6d351df4d970 100644 (file)
@@ -394,38 +394,98 @@ private:
   //===--------------------------------------------------------------------===//
   // Lexing and parsing of C++ inline methods.
 
-  typedef llvm::SmallVector<Token, 32> TokensTy;
   struct LexedMethod {
     Action::DeclTy *D;
-    TokensTy Toks;
+    CachedTokens Toks;
     explicit LexedMethod(Action::DeclTy *MD) : D(MD) {}
   };
 
+  /// LateParsedDefaultArgument - Keeps track of a parameter that may
+  /// have a default argument that cannot be parsed yet because it
+  /// occurs within a member function declaration inside the class
+  /// (C++ [class.mem]p2).
+  struct LateParsedDefaultArgument {
+    explicit LateParsedDefaultArgument(Action::DeclTy *P, 
+                                       CachedTokens *Toks = 0)
+      : Param(P), Toks(Toks) { }
+
+    /// Param - The parameter declaration for this parameter.
+    Action::DeclTy *Param;
+
+    /// Toks - The sequence of tokens that comprises the default
+    /// argument expression, not including the '=' or the terminating
+    /// ')' or ','. This will be NULL for parameters that have no
+    /// default argument.
+    CachedTokens *Toks;
+  };
+  
+  /// LateParsedMethodDeclaration - A method declaration inside a class that
+  /// contains at least one entity whose parsing needs to be delayed
+  /// until the class itself is completely-defined, such as a default
+  /// argument (C++ [class.mem]p2).
+  struct LateParsedMethodDeclaration {
+    explicit LateParsedMethodDeclaration(Action::DeclTy *M) : Method(M) { }
+
+    /// Method - The method declaration.
+    Action::DeclTy *Method;
+
+    /// DefaultArgs - Contains the parameters of the function and
+    /// their default arguments. At least one of the parameters will
+    /// have a default argument, but all of the parameters of the
+    /// method will be stored so that they can be reintroduced into
+    /// scope at the appropriate times. 
+    llvm::SmallVector<LateParsedDefaultArgument, 8> DefaultArgs;
+  };
+
+  /// LateParsedMethodDecls - During parsing of a top (non-nested) C++
+  /// class, its method declarations that contain parts that won't be
+  /// parsed until after the definiton is completed (C++ [class.mem]p2),
+  /// the method declarations will be stored here with the tokens that
+  /// will be parsed to create those entities.
+  typedef std::list<LateParsedMethodDeclaration> LateParsedMethodDecls;
+
   /// LexedMethodsForTopClass - During parsing of a top (non-nested) C++ class,
   /// its inline method definitions and the inline method definitions of its
   /// nested classes are lexed and stored here.
-  typedef std::stack<LexedMethod> LexedMethodsForTopClass;
+  typedef std::list<LexedMethod> LexedMethodsForTopClass;
+
+
+  /// TopClass - Contains information about parts of the top
+  /// (non-nested) C++ class that will need to be parsed after the
+  /// class is fully defined.
+  struct TopClass {
+    /// MethodDecls - Method declarations that contain pieces whose
+    /// parsing will be delayed until the class is fully defined.
+    LateParsedMethodDecls MethodDecls;
+
+    /// MethodDefs - Methods whose definitions will be parsed once the
+    /// class has been fully defined.
+    LexedMethodsForTopClass MethodDefs;
+  };
 
-  /// TopClassStacks - This is initialized with one LexedMethodsForTopClass used
+  /// TopClassStacks - This is initialized with one TopClass used
   /// for lexing all top classes, until a local class in an inline method is
-  /// encountered, at which point a new LexedMethodsForTopClass is pushed here
+  /// encountered, at which point a new TopClass is pushed here
   /// and used until the parsing of that local class is finished.
-  std::stack<LexedMethodsForTopClass> TopClassStacks;
+  std::stack<TopClass> TopClassStacks;
 
-  LexedMethodsForTopClass &getCurTopClassStack() {
+  TopClass &getCurTopClassStack() {
     assert(!TopClassStacks.empty() && "No lexed method stacks!");
     return TopClassStacks.top();
   }
 
   void PushTopClassStack() {
-    TopClassStacks.push(LexedMethodsForTopClass());
+    TopClassStacks.push(TopClass());
   }
   void PopTopClassStack() { TopClassStacks.pop(); }
 
   DeclTy *ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D);
+  void ParseLexedMethodDeclarations();
   void ParseLexedMethodDefs();
-  bool ConsumeAndStoreUntil(tok::TokenKind T, TokensTy &Toks,
-                            tok::TokenKind EarlyAbortIf = tok::unknown);
+  bool ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2, 
+                            CachedTokens &Toks,
+                            tok::TokenKind EarlyAbortIf = tok::unknown,
+                            bool ConsumeFinalToken = true);
 
   //===--------------------------------------------------------------------===//
   // C99 6.9: External Definitions.
index 824847af63b09689aa97ffc94eb27d8a91ee8aaa..a9712fe9ed406960c5de4daf0cebd85547be1c17 100644 (file)
@@ -31,13 +31,13 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) {
 
   // Consume the tokens and store them for later parsing.
 
-  getCurTopClassStack().push(LexedMethod(FnD));
-  TokensTy &Toks = getCurTopClassStack().top().Toks;
+  getCurTopClassStack().MethodDefs.push_back(LexedMethod(FnD));
+  CachedTokens &Toks = getCurTopClassStack().MethodDefs.back().Toks;
 
   // We may have a constructor initializer here.
   if (Tok.is(tok::colon)) {
     // Consume everything up to (and including) the left brace.
-    if (!ConsumeAndStoreUntil(tok::l_brace, Toks, tok::semi)) {
+    if (!ConsumeAndStoreUntil(tok::l_brace, tok::unknown, Toks, tok::semi)) {
       // We didn't find the left-brace we expected after the
       // constructor initializer. 
       if (Tok.is(tok::semi)) {
@@ -45,7 +45,7 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) {
         // don't try to parse this method later.
         Diag(Tok.getLocation(), diag::err_expected_lbrace);
         ConsumeAnyToken();
-        getCurTopClassStack().pop();
+        getCurTopClassStack().MethodDefs.pop_back();
         return FnD;
       }
     }
@@ -56,17 +56,66 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) {
     ConsumeBrace();
   }
   // Consume everything up to (and including) the matching right brace.
-  ConsumeAndStoreUntil(tok::r_brace, Toks);
+  ConsumeAndStoreUntil(tok::r_brace, tok::unknown, Toks);
 
   return FnD;
 }
 
+/// ParseLexedMethodDeclarations - We finished parsing the member
+/// specification of a top (non-nested) C++ class. Now go over the
+/// stack of method declarations with some parts for which parsing was
+/// delayed (such as default arguments) and parse them.
+void Parser::ParseLexedMethodDeclarations() {
+  for (; !getCurTopClassStack().MethodDecls.empty();
+       getCurTopClassStack().MethodDecls.pop_front()) {
+    LateParsedMethodDeclaration &LM = getCurTopClassStack().MethodDecls.front();
+    
+    // Start the delayed C++ method declaration
+    Actions.ActOnStartDelayedCXXMethodDeclaration(CurScope, LM.Method);
+
+    // Introduce the parameters into scope and parse their default
+    // arguments.
+    ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+    for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
+      // Introduce the parameter into scope.
+      Actions.ActOnDelayedCXXMethodParameter(CurScope, LM.DefaultArgs[I].Param);
+
+      if (CachedTokens *Toks = LM.DefaultArgs[I].Toks) {
+        // Parse the default argument from its saved token stream.
+        Toks->push_back(Tok); // So that the current token doesn't get lost
+        PP.EnterTokenStream(&Toks->front(), Toks->size(), true, false);
+
+        // Consume the previously-pushed token.
+        ConsumeAnyToken();
+
+        // Consume the '='.
+        assert(Tok.is(tok::equal) && "Default argument not starting with '='");
+        SourceLocation EqualLoc = ConsumeToken();
+
+        OwningExprResult DefArgResult(ParseAssignmentExpression());
+        if (DefArgResult.isInvalid())
+          Actions.ActOnParamDefaultArgumentError(LM.DefaultArgs[I].Param);
+        else
+          Actions.ActOnParamDefaultArgument(LM.DefaultArgs[I].Param, EqualLoc,
+                                            DefArgResult.release());
+        delete Toks;
+        LM.DefaultArgs[I].Toks = 0;
+      }
+    }
+    PrototypeScope.Exit();
+
+    // Finish the delayed C++ method declaration.
+    Actions.ActOnFinishDelayedCXXMethodDeclaration(CurScope, LM.Method);
+  }
+}
+
 /// ParseLexedMethodDefs - We finished parsing the member specification of a top
 /// (non-nested) C++ class. Now go over the stack of lexed methods that were
 /// collected during its parsing and parse them all.
 void Parser::ParseLexedMethodDefs() {
-  for (; !getCurTopClassStack().empty(); getCurTopClassStack().pop()) {
-    LexedMethod &LM = getCurTopClassStack().top();
+  for (; !getCurTopClassStack().MethodDefs.empty(); 
+       getCurTopClassStack().MethodDefs.pop_front()) {
+    LexedMethod &LM = getCurTopClassStack().MethodDefs.front();
 
     assert(!LM.Toks.empty() && "Empty body!");
     // Append the current token at the end of the new token stream so that it
@@ -92,21 +141,26 @@ void Parser::ParseLexedMethodDefs() {
 }
 
 /// ConsumeAndStoreUntil - Consume and store the token at the passed token
-/// container until the token 'T' is reached (which gets consumed/stored too).
+/// container until the token 'T' is reached (which gets
+/// consumed/stored too, if ConsumeFinalToken). 
 /// If EarlyAbortIf is specified, then we will stop early if we find that
 /// token at the top level.
-/// Returns true if token 'T' was found.
+/// Returns true if token 'T1' or 'T2' was found.
 /// NOTE: This is a specialized version of Parser::SkipUntil.
-bool Parser::ConsumeAndStoreUntil(tok::TokenKind T, TokensTy &Toks,
-                                  tok::TokenKind EarlyAbortIf) {
+bool Parser::ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2,
+                                  CachedTokens &Toks,
+                                  tok::TokenKind EarlyAbortIf,
+                                  bool ConsumeFinalToken) {
   // We always want this function to consume at least one token if the first
   // token isn't T and if not at EOF.
   bool isFirstTokenConsumed = true;
   while (1) {
     // If we found one of the tokens, stop and return true.
-    if (Tok.is(T)) {
-      Toks.push_back(Tok);
-      ConsumeAnyToken();
+    if (Tok.is(T1) || Tok.is(T2)) {
+      if (ConsumeFinalToken) {
+        Toks.push_back(Tok);
+        ConsumeAnyToken();
+      }
       return true;
     }
 
@@ -123,19 +177,19 @@ bool Parser::ConsumeAndStoreUntil(tok::TokenKind T, TokensTy &Toks,
       // Recursively consume properly-nested parens.
       Toks.push_back(Tok);
       ConsumeParen();
-      ConsumeAndStoreUntil(tok::r_paren, Toks);
+      ConsumeAndStoreUntil(tok::r_paren, tok::unknown, Toks);
       break;
     case tok::l_square:
       // Recursively consume properly-nested square brackets.
       Toks.push_back(Tok);
       ConsumeBracket();
-      ConsumeAndStoreUntil(tok::r_square, Toks);
+      ConsumeAndStoreUntil(tok::r_square, tok::unknown, Toks);
       break;
     case tok::l_brace:
       // Recursively consume properly-nested braces.
       Toks.push_back(Tok);
       ConsumeBrace();
-      ConsumeAndStoreUntil(tok::r_brace, Toks);
+      ConsumeAndStoreUntil(tok::r_brace, tok::unknown, Toks);
       break;
 
     // Okay, we found a ']' or '}' or ')', which we think should be balanced.
index ed151402744e34e165fedc90464b399b45f0e5b3..1a8e5d9f95567e76401a3bfc747965fccd25222f 100644 (file)
@@ -1771,6 +1771,10 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
     // Remember this parsed parameter in ParamInfo.
     IdentifierInfo *ParmII = ParmDecl.getIdentifier();
     
+    // DefArgToks is used when the parsing of default arguments needs
+    // to be delayed.
+    CachedTokens *DefArgToks = 0;
+
     // If no parameter was specified, verify that *something* was specified,
     // otherwise we have a missing type and identifier.
     if (DS.getParsedSpecifiers() == DeclSpec::PQ_None && 
@@ -1790,24 +1794,39 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
       // ActOnParamDefaultArgument will reject the default argument in
       // C.
       if (Tok.is(tok::equal)) {
-        SourceLocation EqualLoc = Tok.getLocation();
-        
-        // Consume the '='.
-        ConsumeToken();
-        
         // Parse the default argument
-        OwningExprResult DefArgResult(ParseAssignmentExpression());
-        if (DefArgResult.isInvalid()) {
-          SkipUntil(tok::comma, tok::r_paren, true, true);
+        if (D.getContext() == Declarator::MemberContext) {
+          // If we're inside a class definition, cache the tokens
+          // corresponding to the default argument. We'll actually parse
+          // them when we see the end of the class definition.
+          // FIXME: Templates will require something similar.
+          // FIXME: Can we use a smart pointer for Toks?
+          DefArgToks = new CachedTokens;
+
+          if (!ConsumeAndStoreUntil(tok::comma, tok::r_paren, *DefArgToks, 
+                                    tok::semi, false)) {
+            delete DefArgToks;
+            DefArgToks = 0;
+          } 
         } else {
-          // Inform the actions module about the default argument
-          Actions.ActOnParamDefaultArgument(Param, EqualLoc,
-                                            DefArgResult.release());
+          // Consume the '='.
+          SourceLocation EqualLoc = ConsumeToken();
+        
+          OwningExprResult DefArgResult(ParseAssignmentExpression());
+          if (DefArgResult.isInvalid()) {
+            Actions.ActOnParamDefaultArgumentError(Param);
+            SkipUntil(tok::comma, tok::r_paren, true, true);
+          } else {
+            // Inform the actions module about the default argument
+            Actions.ActOnParamDefaultArgument(Param, EqualLoc,
+                                              DefArgResult.release());
+          }
         }
       }
       
       ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, 
-                             ParmDecl.getIdentifierLoc(), Param));
+                                          ParmDecl.getIdentifierLoc(), Param, 
+                                          DefArgToks));
     }
 
     // If the next token is a comma, consume it and keep reading arguments.
index edcbfad6f371429f9d6e46ae4d24092f33e01089..377186713e71369456ec8dc0ddd5ccdfd237d679 100644 (file)
@@ -533,6 +533,40 @@ Parser::DeclTy *Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) {
                                                        Init.release(),
                                                        LastDeclInGroup);
 
+    if (DeclaratorInfo.isFunctionDeclarator() &&
+        DeclaratorInfo.getDeclSpec().getStorageClassSpec() 
+          != DeclSpec::SCS_typedef) {
+      // We just declared a member function. If this member function
+      // has any default arguments, we'll need to parse them later.
+      LateParsedMethodDeclaration *LateMethod = 0;
+      DeclaratorChunk::FunctionTypeInfo &FTI 
+        = DeclaratorInfo.getTypeObject(0).Fun;
+      for (unsigned ParamIdx = 0; ParamIdx < FTI.NumArgs; ++ParamIdx) {
+        if (LateMethod || FTI.ArgInfo[ParamIdx].DefaultArgTokens) {
+          if (!LateMethod) {
+            // Push this method onto the stack of late-parsed method
+            // declarations.
+            getCurTopClassStack().MethodDecls.push_back(
+                                   LateParsedMethodDeclaration(LastDeclInGroup));
+            LateMethod = &getCurTopClassStack().MethodDecls.back();
+
+            // Add all of the parameters prior to this one (they don't
+            // have default arguments).
+            LateMethod->DefaultArgs.reserve(FTI.NumArgs);
+            for (unsigned I = 0; I < ParamIdx; ++I)
+              LateMethod->DefaultArgs.push_back(
+                        LateParsedDefaultArgument(FTI.ArgInfo[ParamIdx].Param));
+          }
+
+          // Add this parameter to the list of parameters (it or may
+          // not have a default argument).
+          LateMethod->DefaultArgs.push_back(
+            LateParsedDefaultArgument(FTI.ArgInfo[ParamIdx].Param,
+                                      FTI.ArgInfo[ParamIdx].DefaultArgTokens));
+        }
+      }
+    }
+
     // If we don't have a comma, it is either the end of the list (a ';')
     // or an error, bail out.
     if (Tok.isNot(tok::comma))
@@ -642,10 +676,13 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
   // exception-specifications, and constructor ctor-initializers (including
   // such things in nested classes).
   //
-  // FIXME: Only function bodies are parsed correctly, fix the rest.
+  // FIXME: Only function bodies and constructor ctor-initializers are
+  // parsed correctly, fix the rest.
   if (!CurScope->getParent()->isCXXClassScope()) {
     // We are not inside a nested class. This class and its nested classes
-    // are complete and we can parse the lexed inline method definitions.
+    // are complete and we can parse the delayed portions of method
+    // declarations and the lexed inline method definitions.
+    ParseLexedMethodDeclarations();
     ParseLexedMethodDefs();
 
     // For a local class of inline method, pop the LexedMethodsForTopClass that
index 6ce3ec5dd390804f23e755944b5031803647daff..3e4228071c0a08de5102a22a16c1fd817866e12d 100644 (file)
@@ -276,6 +276,7 @@ public:
   virtual void ActOnParamDefaultArgument(DeclTy *param, 
                                          SourceLocation EqualLoc,
                                          ExprTy *defarg);
+  virtual void ActOnParamDefaultArgumentError(DeclTy *param);
   void AddInitializerToDecl(DeclTy *dcl, ExprArg init);
   void ActOnUninitializedDecl(DeclTy *dcl);
   virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group);
@@ -960,9 +961,13 @@ public:
 
   virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl);
   
-  
+  virtual void ActOnStartDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method);
+  virtual void ActOnDelayedCXXMethodParameter(Scope *S, DeclTy *Param);
+  virtual void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method);
+
   bool CheckConstructorDeclarator(Declarator &D, QualType &R,
                                   FunctionDecl::StorageClass& SC);
+  bool CheckConstructor(CXXConstructorDecl *Constructor);
   bool CheckDestructorDeclarator(Declarator &D, QualType &R,
                                  FunctionDecl::StorageClass& SC);
   bool CheckConversionDeclarator(Declarator &D, QualType &R,
index fe9ae07ebaca54b417c803dfb498c68986262d5c..f023fbf2e2a8c1b110c90ce84709f6168e1bf5b0 100644 (file)
@@ -134,7 +134,8 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
   assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");\r
   assert(PreDeclaratorDC == 0 && "Previous declarator context not popped?");\r
   PreDeclaratorDC = static_cast<DeclContext*>(S->getEntity());\r
-  S->setEntity(static_cast<DeclContext*>(SS.getScopeRep()));\r
+  CurContext = static_cast<DeclContext*>(SS.getScopeRep());\r
+  S->setEntity(CurContext);\r
 }\r
 \r
 /// ActOnCXXExitDeclaratorScope - Called when a declarator that previously\r
@@ -147,4 +148,9 @@ void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
   assert(S->getEntity() == SS.getScopeRep() && "Context imbalance!");\r
   S->setEntity(PreDeclaratorDC);\r
   PreDeclaratorDC = 0;\r
+\r
+  // Reset CurContext to the nearest enclosing context.\r
+  while (!S->getEntity() && S->getParent())\r
+    S = S->getParent();\r
+  CurContext = static_cast<DeclContext*>(S->getEntity());\r
 }\r
index 0d48c626125e21d3ca629e34bacf680f694e9a23..81a4abea7f36ce1652bd90545e4e79543da997ae 100644 (file)
@@ -1230,30 +1230,8 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl,
       }
     }
 
-    if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(NewFD)) {
-      CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(DC);
-
-      // C++ [class.copy]p3:
-      //   A declaration of a constructor for a class X is ill-formed if
-      //   its first parameter is of type (optionally cv-qualified) X and
-      //   either there are no other parameters or else all other
-      //   parameters have default arguments.
-      if ((Constructor->getNumParams() == 1) || 
-          (Constructor->getNumParams() > 1 && 
-           Constructor->getParamDecl(1)->getDefaultArg() != 0)) {
-        QualType ParamType = Constructor->getParamDecl(0)->getType();
-        QualType ClassTy = Context.getTagDeclType(ClassDecl);
-        if (Context.getCanonicalType(ParamType).getUnqualifiedType() 
-              == ClassTy) {
-          Diag(Constructor->getLocation(), diag::err_constructor_byvalue_arg)
-            << SourceRange(Constructor->getParamDecl(0)->getLocation());
-          Constructor->setInvalidDecl();
-        }
-      }
-
-      // Notify the class that we've added a constructor.
-      ClassDecl->addedConstructor(Context, Constructor);
-    }
+    if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(NewFD))
+      InvalidDecl = InvalidDecl || CheckConstructor(Constructor);
     else if (isa<CXXDestructorDecl>(NewFD))
       cast<CXXRecordDecl>(NewFD->getParent())->setUserDeclaredDestructor(true);
     else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
@@ -2865,6 +2843,9 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD,
                               DeclSpec::SCS_mutable,
                             /*PrevDecl=*/0);
 
+  if (getLangOptions().CPlusPlus)
+    CheckExtraCXXDefaultArguments(D);
+
   ProcessDeclAttributes(NewFD, D);
 
   if (D.getInvalidType() || InvalidDecl)
index 6f1683ee7f39fb4c2718ad22c50e0226093985bc..184ba15dd624b42c39faea3ad374a99fb5a112e9 100644 (file)
@@ -114,6 +114,7 @@ Sema::ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc,
   if (!getLangOptions().CPlusPlus) {
     Diag(EqualLoc, diag::err_param_default_argument)
       << DefaultArg->getSourceRange();
+    Param->setInvalidDecl();
     return;
   }
 
@@ -136,13 +137,21 @@ Sema::ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc,
 
   // Check that the default argument is well-formed
   CheckDefaultArgumentVisitor DefaultArgChecker(DefaultArg.get(), this);
-  if (DefaultArgChecker.Visit(DefaultArg.get()))
+  if (DefaultArgChecker.Visit(DefaultArg.get())) {
+    Param->setInvalidDecl();
     return;
+  }
 
   // Okay: add the default argument to the parameter
   Param->setDefaultArg(DefaultArg.take());
 }
 
+/// ActOnParamDefaultArgumentError - Parsing or semantic analysis of
+/// the default argument for the parameter param failed.
+void Sema::ActOnParamDefaultArgumentError(DeclTy *param) {
+  ((ParmVarDecl*)param)->setInvalidDecl();
+}
+
 /// CheckExtraCXXDefaultArguments - Check for any extra default
 /// arguments in the declarator, which is not a function declaration
 /// or definition and therefore is not permitted to have default
@@ -165,6 +174,12 @@ void Sema::CheckExtraCXXDefaultArguments(Declarator &D) {
           Diag(Param->getLocation(), diag::err_param_default_argument_nonfunc)
             << Param->getDefaultArg()->getSourceRange();
           Param->setDefaultArg(0);
+        } else if (CachedTokens *Toks 
+                     = chunk.Fun.ArgInfo[argIdx].DefaultArgTokens) {
+          Diag(Param->getLocation(), diag::err_param_default_argument_nonfunc)
+            << SourceRange((*Toks)[1].getLocation(), Toks->back().getLocation());
+          delete Toks;
+          chunk.Fun.ArgInfo[argIdx].DefaultArgTokens = 0;
         }
       }
     }
@@ -231,7 +246,9 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) {
   for(; p < NumParams; ++p) {
     ParmVarDecl *Param = FD->getParamDecl(p);
     if (!Param->getDefaultArg()) {
-      if (Param->getIdentifier())
+      if (Param->isInvalidDecl())
+        /* We already complained about this parameter. */;
+      else if (Param->getIdentifier())
         Diag(Param->getLocation(), 
              diag::err_param_default_argument_missing_name)
           << Param->getIdentifier();
@@ -401,6 +418,7 @@ void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) {
 /// any. 'LastInGroup' is non-null for cases where one declspec has multiple
 /// declarators on it.
 ///
+/// FIXME: The note below is out-of-date.
 /// NOTE: Because of CXXFieldDecl's inability to be chained like ScopedDecls, if
 /// an instance field is declared, a new CXXFieldDecl is created but the method
 /// does *not* return it; it returns LastInGroup instead. The other C++ members
@@ -875,8 +893,60 @@ void Sema::ActOnFinishCXXClassDef(DeclTy *D) {
   Consumer.HandleTagDeclDefinition(Rec);
 }
 
+/// ActOnStartDelayedCXXMethodDeclaration - We have completed
+/// parsing a top-level (non-nested) C++ class, and we are now
+/// parsing those parts of the given Method declaration that could
+/// not be parsed earlier (C++ [class.mem]p2), such as default
+/// arguments. This action should enter the scope of the given
+/// Method declaration as if we had just parsed the qualified method
+/// name. However, it should not bring the parameters into scope;
+/// that will be performed by ActOnDelayedCXXMethodParameter.
+void Sema::ActOnStartDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method) {
+  CXXScopeSpec SS;
+  SS.setScopeRep(((FunctionDecl*)Method)->getDeclContext());
+  ActOnCXXEnterDeclaratorScope(S, SS);
+}
+
+/// ActOnDelayedCXXMethodParameter - We've already started a delayed
+/// C++ method declaration. We're (re-)introducing the given
+/// function parameter into scope for use in parsing later parts of
+/// the method declaration. For example, we could see an
+/// ActOnParamDefaultArgument event for this parameter.
+void Sema::ActOnDelayedCXXMethodParameter(Scope *S, DeclTy *ParamD) {
+  ParmVarDecl *Param = (ParmVarDecl*)ParamD;
+  S->AddDecl(Param);
+  if (Param->getDeclName())
+    IdResolver.AddDecl(Param);
+}
+
+/// ActOnFinishDelayedCXXMethodDeclaration - We have finished
+/// processing the delayed method declaration for Method. The method
+/// declaration is now considered finished. There may be a separate
+/// ActOnStartOfFunctionDef action later (not necessarily
+/// immediately!) for this method, if it was also defined inside the
+/// class body.
+void Sema::ActOnFinishDelayedCXXMethodDeclaration(Scope *S, DeclTy *MethodD) {
+  FunctionDecl *Method = (FunctionDecl*)MethodD;
+  CXXScopeSpec SS;
+  SS.setScopeRep(Method->getDeclContext());
+  ActOnCXXExitDeclaratorScope(S, SS);
+
+  // Now that we have our default arguments, check the constructor
+  // again. It could produce additional diagnostics or affect whether
+  // the class has implicitly-declared destructors, among other
+  // things.
+  if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Method)) {
+    if (CheckConstructor(Constructor))
+      Constructor->setInvalidDecl();
+  }
+
+  // Check the default arguments, which we may have added.
+  if (!Method->isInvalidDecl())
+    CheckCXXDefaultArguments(Method);
+}
+
 /// CheckConstructorDeclarator - Called by ActOnDeclarator to check
-/// the well-formednes of the constructor declarator @p D with type @p
+/// the well-formedness of the constructor declarator @p D with type @p
 /// R. If there are any errors in the declarator, this routine will
 /// emit diagnostics and return true. Otherwise, it will return
 /// false. Either way, the type @p R will be updated to reflect a
@@ -944,6 +1014,39 @@ bool Sema::CheckConstructorDeclarator(Declarator &D, QualType &R,
   return isInvalid;
 }
 
+/// CheckConstructor - Checks a fully-formed constructor for
+/// well-formedness, issuing any diagnostics required. Returns true if
+/// the constructor declarator is invalid.
+bool Sema::CheckConstructor(CXXConstructorDecl *Constructor) {
+  if (Constructor->isInvalidDecl())
+    return true;
+
+  CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(Constructor->getDeclContext());
+  bool Invalid = false;
+
+  // C++ [class.copy]p3:
+  //   A declaration of a constructor for a class X is ill-formed if
+  //   its first parameter is of type (optionally cv-qualified) X and
+  //   either there are no other parameters or else all other
+  //   parameters have default arguments.
+  if ((Constructor->getNumParams() == 1) || 
+      (Constructor->getNumParams() > 1 && 
+       Constructor->getParamDecl(1)->getDefaultArg() != 0)) {
+    QualType ParamType = Constructor->getParamDecl(0)->getType();
+    QualType ClassTy = Context.getTagDeclType(ClassDecl);
+    if (Context.getCanonicalType(ParamType).getUnqualifiedType() == ClassTy) {
+      Diag(Constructor->getLocation(), diag::err_constructor_byvalue_arg)
+        << SourceRange(Constructor->getParamDecl(0)->getLocation());
+      Invalid = true;
+    }
+  }
+  
+  // Notify the class that we've added a constructor.
+  ClassDecl->addedConstructor(Context, Constructor);
+
+  return Invalid;
+}
+
 /// CheckDestructorDeclarator - Called by ActOnDeclarator to check
 /// the well-formednes of the destructor declarator @p D with type @p
 /// R. If there are any errors in the declarator, this routine will
index e5fe48d1cb754c878f78b92c10b6f502dc309e65..863ac0e25fd0a3f89bdeb36f0f1a3347ae9bc161 100644 (file)
@@ -63,17 +63,48 @@ void C::h() {
 }
 
 // C++ [dcl.fct.default]p9
-class Y { 
+struct Y { 
   int a; 
   int mem1(int i = a); // expected-error{{invalid use of nonstatic data member 'a'}}
-  // FIXME: The code below is well-formed.
-  //  int mem2(int i = b); // OK; use X::b 
+  int mem2(int i = b); // OK; use Y::b 
   int mem3(int i);
   int mem4(int i);
+
+  struct Nested {
+    int mem5(int i = b, // OK; use Y::b
+             int j = c, // OK; use Y::Nested::c
+             int k = j, // expected-error{{default argument references parameter 'j'}}
+             int l = a,  // expected-error{{invalid use of nonstatic data member 'a'}}
+             Nested* self = this, // expected-error{{invalid use of 'this' outside of a nonstatic member function}}
+             int m); // expected-error{{missing default argument on parameter 'm'}}
+    static int c;
+  };
+
   static int b; 
+
+  int (*f)(int = 17); // expected-error{{default arguments can only be specified for parameters in a function declaration}}
+
+  void mem8(int (*fp)(int) = (int (*)(int = 17))0); // expected-error{{default arguments can only be specified for parameters in a function declaration}}
 }; 
 
 int Y::mem3(int i = b) { return i; } // OK; use X::b
 
 int Y::mem4(int i = a) // expected-error{{invalid use of nonstatic data member 'a'}}
 { return i; }
+
+
+// Try to verify that default arguments interact properly with copy
+// constructors.
+class Z {
+public:
+  Z(Z&, int i = 17); // expected-note{{candidate function}}
+
+  void f(Z& z) { 
+    Z z2;    // expected-error{{no matching constructor for initialization}}
+    Z z3(z);
+  }
+};
+
+void test_Z(const Z& z) {
+  Z z2(z); // expected-error{{no matching constructor for initialization of 'z2'}}
+}