From 72b505b7904b3c9320a1312998800ba76e4f5841 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 16 Dec 2008 21:30:33 +0000 Subject: [PATCH] Delay parsing of default arguments of member functions until the class 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 | 34 ++++++++- include/clang/Parse/DeclSpec.h | 20 ++++- include/clang/Parse/Parser.h | 80 +++++++++++++++++--- lib/Parse/ParseCXXInlineMethods.cpp | 88 +++++++++++++++++----- lib/Parse/ParseDecl.cpp | 43 ++++++++--- lib/Parse/ParseDeclCXX.cpp | 41 ++++++++++- lib/Sema/Sema.h | 7 +- lib/Sema/SemaCXXScopeSpec.cpp | 8 +- lib/Sema/SemaDecl.cpp | 29 ++------ lib/Sema/SemaDeclCXX.cpp | 109 +++++++++++++++++++++++++++- test/SemaCXX/default2.cpp | 37 +++++++++- 11 files changed, 419 insertions(+), 77 deletions(-) diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index af72554908..2bd2e4b22f 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -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. diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index ab52a298fa..4a97fd60ed 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -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 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. diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index ba5cc84b99..579d52303c 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -394,38 +394,98 @@ private: //===--------------------------------------------------------------------===// // Lexing and parsing of C++ inline methods. - typedef llvm::SmallVector 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 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 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 LexedMethodsForTopClass; + typedef std::list LexedMethodsForTopClass; - /// TopClassStacks - This is initialized with one LexedMethodsForTopClass used + + /// 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 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 TopClassStacks; + std::stack 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. diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index 824847af63..a9712fe9ed 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -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. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index ed15140274..1a8e5d9f95 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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. diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index edcbfad6f3..377186713e 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -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 diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 6ce3ec5dd3..3e4228071c 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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, diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index fe9ae07eba..f023fbf2e2 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -134,7 +134,8 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) { assert(SS.isSet() && "Parser passed invalid CXXScopeSpec."); assert(PreDeclaratorDC == 0 && "Previous declarator context not popped?"); PreDeclaratorDC = static_cast(S->getEntity()); - S->setEntity(static_cast(SS.getScopeRep())); + CurContext = static_cast(SS.getScopeRep()); + S->setEntity(CurContext); } /// ActOnCXXExitDeclaratorScope - Called when a declarator that previously @@ -147,4 +148,9 @@ void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) { assert(S->getEntity() == SS.getScopeRep() && "Context imbalance!"); S->setEntity(PreDeclaratorDC); PreDeclaratorDC = 0; + + // Reset CurContext to the nearest enclosing context. + while (!S->getEntity() && S->getParent()) + S = S->getParent(); + CurContext = static_cast(S->getEntity()); } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 0d48c62612..81a4abea7f 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1230,30 +1230,8 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, } } - if (CXXConstructorDecl *Constructor = dyn_cast(NewFD)) { - CXXRecordDecl *ClassDecl = cast(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(NewFD)) + InvalidDecl = InvalidDecl || CheckConstructor(Constructor); else if (isa(NewFD)) cast(NewFD->getParent())->setUserDeclaredDestructor(true); else if (CXXConversionDecl *Conversion = dyn_cast(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) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 6f1683ee7f..184ba15dd6 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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(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(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 diff --git a/test/SemaCXX/default2.cpp b/test/SemaCXX/default2.cpp index e5fe48d1cb..863ac0e25f 100644 --- a/test/SemaCXX/default2.cpp +++ b/test/SemaCXX/default2.cpp @@ -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'}} +}