From e8381c00eb6c753c6100b968d16c829933e743e2 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 5 Nov 2008 04:29:56 +0000 Subject: [PATCH] Initial implementation of parsing, semantic analysis, and AST-building for constructor initializations, e.g., class A { }; class B : public A { int m; public: B() : A(), m(17) { }; }; llvm-svn: 58749 --- clang/include/clang/AST/DeclCXX.h | 99 ++++++++++++++++ clang/include/clang/Basic/DiagnosticKinds.def | 10 ++ clang/include/clang/Parse/Action.h | 24 ++++ clang/include/clang/Parse/Parser.h | 14 ++- clang/lib/AST/DeclCXX.cpp | 33 ++++++ clang/lib/Parse/ParseCXXInlineMethods.cpp | 22 +++- clang/lib/Parse/ParseDeclCXX.cpp | 97 +++++++++++++++- clang/lib/Parse/Parser.cpp | 21 +++- clang/lib/Sema/Sema.h | 9 ++ clang/lib/Sema/SemaDeclCXX.cpp | 106 ++++++++++++++++++ clang/lib/Sema/SemaExprCXX.cpp | 2 +- .../test/SemaCXX/constructor-initializer.cpp | 43 +++++++ clang/www/cxx_status.html | 10 +- 13 files changed, 473 insertions(+), 17 deletions(-) create mode 100644 clang/test/SemaCXX/constructor-initializer.cpp diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 7a0bce8f83f9..3d0c805a3a7a 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -395,6 +395,105 @@ protected: friend Decl* Decl::Create(llvm::Deserializer& D, ASTContext& C); }; +/// CXXBaseOrMemberInitializer - Represents a C++ base or member +/// initializer, which is part of a constructor initializer that +/// initializes one non-static member variable or one base class. For +/// example, in the following, both 'A(a)' and 'f(3.14159)' are member +/// initializers: +/// +/// @code +/// class A { }; +/// class B : public A { +/// float f; +/// public: +/// B(A& a) : A(a), f(3.14159) { } +/// }; +class CXXBaseOrMemberInitializer { + /// BaseOrMember - This points to the entity being initialized, + /// which is either a base class (a Type) or a non-static data + /// member (a CXXFieldDecl). When the low bit is 1, it's a base + /// class; when the low bit is 0, it's a member. + uintptr_t BaseOrMember; + + /// Args - The arguments used to initialize the base or member. + Expr **Args; + unsigned NumArgs; + +public: + /// CXXBaseOrMemberInitializer - Creates a new base-class initializer. + explicit + CXXBaseOrMemberInitializer(QualType BaseType, Expr **Args, unsigned NumArgs); + + /// CXXBaseOrMemberInitializer - Creates a new member initializer. + explicit + CXXBaseOrMemberInitializer(CXXFieldDecl *Member, Expr **Args, unsigned NumArgs); + + /// ~CXXBaseOrMemberInitializer - Destroy the base or member initializer. + ~CXXBaseOrMemberInitializer(); + + /// arg_iterator - Iterates through the member initialization + /// arguments. + typedef Expr **arg_iterator; + + /// arg_const_iterator - Iterates through the member initialization + /// arguments. + typedef Expr * const * arg_const_iterator; + + /// isBaseInitializer - Returns true when this initializer is + /// initializing a base class. + bool isBaseInitializer() const { return (BaseOrMember & 0x1) != 0; } + + /// isMemberInitializer - Returns true when this initializer is + /// initializing a non-static data member. + bool isMemberInitializer() const { return (BaseOrMember & 0x1) == 0; } + + /// getBaseClass - If this is a base class initializer, returns the + /// type used to specify the initializer. The resulting type will be + /// a class type or a typedef of a class type. If this is not a base + /// class initializer, returns NULL. + Type *getBaseClass() { + if (isBaseInitializer()) + return reinterpret_cast(BaseOrMember & ~0x01); + else + return 0; + } + + /// getBaseClass - If this is a base class initializer, returns the + /// type used to specify the initializer. The resulting type will be + /// a class type or a typedef of a class type. If this is not a base + /// class initializer, returns NULL. + const Type *getBaseClass() const { + if (isBaseInitializer()) + return reinterpret_cast(BaseOrMember & ~0x01); + else + return 0; + } + + /// getMember - If this is a member initializer, returns the + /// declaration of the non-static data member being + /// initialized. Otherwise, returns NULL. + CXXFieldDecl *getMember() { + if (isMemberInitializer()) + return reinterpret_cast(BaseOrMember); + else + return 0; + } + + /// begin() - Retrieve an iterator to the first initializer argument. + arg_iterator begin() { return Args; } + /// begin() - Retrieve an iterator to the first initializer argument. + arg_const_iterator begin() const { return Args; } + + /// end() - Retrieve an iterator past the last initializer argument. + arg_iterator end() { return Args + NumArgs; } + /// end() - Retrieve an iterator past the last initializer argument. + arg_const_iterator end() const { return Args + NumArgs; } + + /// getNumArgs - Determine the number of arguments used to + /// initialize the member or base. + unsigned getNumArgs() const { return NumArgs; } +}; + /// CXXConstructorDecl - Represents a C++ constructor within a /// class. For example: /// diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 8517222c1db4..30238f6d6f98 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -562,6 +562,8 @@ DIAG(warn_statement_disambiguation, WARNING, "statement was disambiguated as %0") DIAG(warn_parens_disambiguated_as_function_decl, WARNING, "parentheses were disambiguated as a function declarator") +DIAG(err_expected_member_or_base_name, ERROR, + "expected class member or base class name") // Language specific pragmas @@ -1240,6 +1242,14 @@ DIAG(err_expected_class_name, ERROR, DIAG(err_anon_type_definition, ERROR, "declaration of anonymous %0 must be a definition") +// C++ member initializers. +DIAG(err_mem_init_not_member_or_class, ERROR, + "member initializer '%0' does not name a non-static data member or base class") +DIAG(err_base_init_does_not_name_class, ERROR, + "constructor initializer '%0' does not name a class") +DIAG(err_base_init_direct_and_virtual, ERROR, + "base class initializer '%0' names both a direct base class and an inherited virtual base class") + // Derived classes. DIAG(err_dup_virtual, ERROR, "duplicate 'virtual' in base specifier") diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index aa8710b97b00..345f18609012 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -58,6 +58,7 @@ public: typedef void TypeTy; typedef void AttrTy; typedef void BaseTy; + typedef void MemInitTy; /// ActionResult - This structure is used while parsing/acting on expressions, /// stmts, etc. It encapsulates both the object returned by the action, plus @@ -85,6 +86,7 @@ public: typedef ActionResult<1> StmtResult; typedef ActionResult<2> TypeResult; typedef ActionResult<3> BaseResult; + typedef ActionResult<4> MemInitResult; /// Deletion callbacks - Since the parser doesn't know the concrete types of /// the AST nodes being generated, it must do callbacks to delete objects when @@ -687,6 +689,28 @@ public: return 0; } + virtual MemInitResult ActOnMemInitializer(DeclTy *ConstructorDecl, + Scope *S, + IdentifierInfo *MemberOrBase, + SourceLocation IdLoc, + SourceLocation LParenLoc, + ExprTy **Args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + return true; + } + + /// ActOnMemInitializers - This is invoked when all of the member + /// initializers of a constructor have been parsed. ConstructorDecl + /// is the function declaration (which will be a C++ constructor in + /// a well-formed program), ColonLoc is the location of the ':' that + /// starts the constructor initializer, and MemInit/NumMemInits + /// contains the individual member (and base) initializers. + virtual void ActOnMemInitializers(DeclTy *ConstructorDecl, + SourceLocation ColonLoc, + MemInitTy **MemInits, unsigned NumMemInits) { + } + /// ActOnFinishCXXMemberSpecification - Invoked after all member declarators /// are parsed but *before* parsing of inline method definitions. virtual void ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index bffe66023e05..897c09d0bc42 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -74,7 +74,8 @@ public: typedef Action::DeclTy DeclTy; typedef Action::TypeTy TypeTy; typedef Action::BaseTy BaseTy; - + typedef Action::MemInitTy MemInitTy; + // Parsing methods. /// ParseTranslationUnit - All in one method that initializes parses, and @@ -314,10 +315,11 @@ private: bool SkipUntil(const tok::TokenKind *Toks, unsigned NumToks, bool StopAtSemi = true, bool DontConsume = false); - typedef Action::ExprResult ExprResult; - typedef Action::StmtResult StmtResult; - typedef Action::BaseResult BaseResult; - + typedef Action::ExprResult ExprResult; + typedef Action::StmtResult StmtResult; + typedef Action::BaseResult BaseResult; + typedef Action::MemInitResult MemInitResult; + //===--------------------------------------------------------------------===// // Lexing and parsing of C++ inline methods. @@ -717,6 +719,8 @@ private: void ParseCXXMemberSpecification(SourceLocation StartLoc, unsigned TagType, DeclTy *TagDecl); DeclTy *ParseCXXClassMemberDeclaration(AccessSpecifier AS); + void ParseConstructorInitializer(DeclTy *ConstructorDecl); + MemInitResult ParseMemInitializer(DeclTy *ConstructorDecl); //===--------------------------------------------------------------------===// // C++ 10: Derived classes [class.derived] diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 21701461e617..9f20ac816395 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -109,6 +109,39 @@ QualType CXXMethodDecl::getThisType(ASTContext &C) const { return C.getPointerType(ClassTy).withConst(); } +CXXBaseOrMemberInitializer:: +CXXBaseOrMemberInitializer(QualType BaseType, Expr **Args, unsigned NumArgs) + : Args(0), NumArgs(0) { + BaseOrMember = reinterpret_cast(BaseType.getTypePtr()); + assert((BaseOrMember & 0x01) == 0 && "Invalid base class type pointer"); + BaseOrMember |= 0x01; + + if (NumArgs > 0) { + this->NumArgs = NumArgs; + this->Args = new Expr*[NumArgs]; + for (unsigned Idx = 0; Idx < NumArgs; ++Idx) + this->Args[Idx] = Args[Idx]; + } +} + +CXXBaseOrMemberInitializer:: +CXXBaseOrMemberInitializer(CXXFieldDecl *Member, Expr **Args, unsigned NumArgs) + : Args(0), NumArgs(0) { + BaseOrMember = reinterpret_cast(Member); + assert((BaseOrMember & 0x01) == 0 && "Invalid member pointer"); + + if (NumArgs > 0) { + this->NumArgs = NumArgs; + this->Args = new Expr*[NumArgs]; + for (unsigned Idx = 0; Idx < NumArgs; ++Idx) + this->Args[Idx] = Args[Idx]; + } +} + +CXXBaseOrMemberInitializer::~CXXBaseOrMemberInitializer() { + delete [] Args; +} + CXXConstructorDecl * CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation L, IdentifierInfo *Id, diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index ca0003440fe0..7d977c1251b0 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -23,7 +23,8 @@ Parser::DeclTy * Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) { assert(D.getTypeObject(0).Kind == DeclaratorChunk::Function && "This isn't a function declarator!"); - assert(Tok.is(tok::l_brace) && "Current token not a '{'!"); + assert((Tok.is(tok::l_brace) || Tok.is(tok::colon)) && + "Current token not a '{' or ':'!"); DeclTy *FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0, 0); @@ -32,9 +33,16 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) { getCurTopClassStack().push(LexedMethod(FnD)); TokensTy &Toks = getCurTopClassStack().top().Toks; - // Begin by storing the '{' token. - Toks.push_back(Tok); - ConsumeBrace(); + // We may have a constructor initializer here. + if (Tok.is(tok::colon)) { + // Consume everything up to (and including) the left brace. + ConsumeAndStoreUntil(tok::l_brace, Toks); + } else { + // Begin by storing the '{' token. + Toks.push_back(Tok); + ConsumeBrace(); + } + // Consume everything up to (and including) the matching right brace. ConsumeAndStoreUntil(tok::r_brace, Toks); return FnD; @@ -55,13 +63,17 @@ void Parser::ParseLexedMethodDefs() { // Consume the previously pushed token. ConsumeAnyToken(); - assert(Tok.is(tok::l_brace) && "Inline method not starting with '{'"); + assert((Tok.is(tok::l_brace) || Tok.is(tok::colon)) && + "Inline method not starting with '{' or ':'"); // Parse the method body. Function body parsing code is similar enough // to be re-used for method bodies as well. EnterScope(Scope::FnScope|Scope::DeclScope); Actions.ActOnStartOfFunctionDef(CurScope, LM.D); + if (Tok.is(tok::colon)) + ParseConstructorInitializer(LM.D); + ParseFunctionStatementBody(LM.D, Tok.getLocation(), Tok.getLocation()); getCurTopClassStack().pop(); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 7d03c591139d..57fa193e756e 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -435,7 +435,8 @@ Parser::DeclTy *Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { } // function-definition: - if (Tok.is(tok::l_brace)) { + if (Tok.is(tok::l_brace) + || (DeclaratorInfo.isFunctionDeclarator() && Tok.is(tok::colon))) { if (!DeclaratorInfo.isFunctionDeclarator()) { Diag(Tok, diag::err_func_def_no_params); ConsumeBrace(); @@ -638,3 +639,97 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, Actions.ActOnFinishCXXClassDef(TagDecl); } + +/// ParseConstructorInitializer - Parse a C++ constructor initializer, +/// which explicitly initializes the members or base classes of a +/// class (C++ [class.base.init]). For example, the three initializers +/// after the ':' in the Derived constructor below: +/// +/// @code +/// class Base { }; +/// class Derived : Base { +/// int x; +/// float f; +/// public: +/// Derived(float f) : Base(), x(17), f(f) { } +/// }; +/// @endcode +/// +/// [C++] ctor-initializer: +/// ':' mem-initializer-list +/// +/// [C++] mem-initializer-list: +/// mem-initializer +/// mem-initializer , mem-initializer-list +void Parser::ParseConstructorInitializer(DeclTy *ConstructorDecl) { + assert(Tok.is(tok::colon) && "Constructor initializer always starts with ':'"); + + SourceLocation ColonLoc = ConsumeToken(); + + llvm::SmallVector MemInitializers; + + do { + MemInitResult MemInit = ParseMemInitializer(ConstructorDecl); + if (!MemInit.isInvalid) + MemInitializers.push_back(MemInit.Val); + + if (Tok.is(tok::comma)) + ConsumeToken(); + else if (Tok.is(tok::l_brace)) + break; + else { + // Skip over garbage, until we get to '{'. Don't eat the '{'. + SkipUntil(tok::l_brace, true, true); + break; + } + } while (true); + + Actions.ActOnMemInitializers(ConstructorDecl, ColonLoc, + &MemInitializers[0], MemInitializers.size()); +} + +/// ParseMemInitializer - Parse a C++ member initializer, which is +/// part of a constructor initializer that explicitly initializes one +/// member or base class (C++ [class.base.init]). See +/// ParseConstructorInitializer for an example. +/// +/// [C++] mem-initializer: +/// mem-initializer-id '(' expression-list[opt] ')' +/// +/// [C++] mem-initializer-id: +/// '::'[opt] nested-name-specifier[opt] class-name +/// identifier +Parser::MemInitResult Parser::ParseMemInitializer(DeclTy *ConstructorDecl) { + // FIXME: parse '::'[opt] nested-name-specifier[opt] + + if (Tok.isNot(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_expected_member_or_base_name); + return true; + } + + // Get the identifier. This may be a member name or a class name, + // but we'll let the semantic analysis determine which it is. + IdentifierInfo *II = Tok.getIdentifierInfo(); + SourceLocation IdLoc = ConsumeToken(); + + // Parse the '('. + if (Tok.isNot(tok::l_paren)) { + Diag(Tok.getLocation(), diag::err_expected_lparen); + return true; + } + SourceLocation LParenLoc = ConsumeParen(); + + // Parse the optional expression-list. + ExprListTy ArgExprs; + CommaLocsTy CommaLocs; + if (Tok.isNot(tok::r_paren) && ParseExpressionList(ArgExprs, CommaLocs)) { + SkipUntil(tok::r_paren); + return true; + } + + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + return Actions.ActOnMemInitializer(ConstructorDecl, CurScope, II, IdLoc, + LParenLoc, &ArgExprs[0], ArgExprs.size(), + &CommaLocs[0], RParenLoc); +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 6beaac0151f6..625da6c281e3 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -481,6 +481,10 @@ Parser::DeclTy *Parser::ParseDeclarationOrFunctionDefinition() { /// decl-specs declarator declaration-list[opt] compound-statement /// [C90] function-definition: [C99 6.7.1] - implicit int result /// [C90] decl-specs[opt] declarator declaration-list[opt] compound-statement +/// [C++] function-definition: [C++ 8.4] +/// decl-specifier-seq[opt] declarator ctor-initializer[opt] function-body +/// [C++] function-definition: [C++ 8.4] +/// decl-specifier-seq[opt] declarator function-try-block [TODO] /// Parser::DeclTy *Parser::ParseFunctionDefinition(Declarator &D) { const DeclaratorChunk &FnTypeInfo = D.getTypeObject(0); @@ -504,8 +508,13 @@ Parser::DeclTy *Parser::ParseFunctionDefinition(Declarator &D) { if (!FTI.hasPrototype && FTI.NumArgs != 0) ParseKNRParamDeclarations(D); - // We should have an opening brace now. - if (Tok.isNot(tok::l_brace)) { + if (getLang().CPlusPlus && Tok.is(tok::colon)) { + + } + + // We should have either an opening brace or, in a C++ constructor, + // we may have a colon. + if (Tok.isNot(tok::l_brace) && Tok.isNot(tok::colon)) { Diag(Tok, diag::err_expected_fn_body); // Skip over garbage, until we get to '{'. Don't eat the '{'. @@ -516,8 +525,6 @@ Parser::DeclTy *Parser::ParseFunctionDefinition(Declarator &D) { return 0; } - SourceLocation BraceLoc = Tok.getLocation(); - // Enter a scope for the function body. EnterScope(Scope::FnScope|Scope::DeclScope); @@ -525,6 +532,12 @@ Parser::DeclTy *Parser::ParseFunctionDefinition(Declarator &D) { // specified Declarator for the function. DeclTy *Res = Actions.ActOnStartOfFunctionDef(CurScope, D); + // If we have a colon, then we're probably parsing a C++ + // ctor-initializer. + if (Tok.is(tok::colon)) + ParseConstructorInitializer(Res); + + SourceLocation BraceLoc = Tok.getLocation(); return ParseFunctionStatementBody(Res, BraceLoc, BraceLoc); } diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index ce70855a3784..72b56deca37d 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -809,6 +809,15 @@ public: Declarator &D, ExprTy *BitfieldWidth, ExprTy *Init, DeclTy *LastInGroup); + virtual MemInitResult ActOnMemInitializer(DeclTy *ConstructorD, + Scope *S, + IdentifierInfo *MemberOrBase, + SourceLocation IdLoc, + SourceLocation LParenLoc, + ExprTy **Args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc); + void AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl); virtual void ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index e4321f53ff0a..95d17fa327b7 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "Sema.h" +#include "SemaInherit.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/TypeOrdering.h" @@ -543,6 +544,111 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, return Member; } +/// ActOnMemInitializer - Handle a C++ member initializer. +Sema::MemInitResult +Sema::ActOnMemInitializer(DeclTy *ConstructorD, + Scope *S, + IdentifierInfo *MemberOrBase, + SourceLocation IdLoc, + SourceLocation LParenLoc, + ExprTy **Args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + CXXConstructorDecl *Constructor + = dyn_cast((Decl*)ConstructorD); + if (!Constructor) { + // The user wrote a constructor initializer on a function that is + // not a C++ constructor. Ignore the error for now, because we may + // have more member initializers coming; we'll diagnose it just + // once in ActOnMemInitializers. + return true; + } + + CXXRecordDecl *ClassDecl = Constructor->getParent(); + + // C++ [class.base.init]p2: + // Names in a mem-initializer-id are looked up in the scope of the + // constructor’s class and, if not found in that scope, are looked + // up in the scope containing the constructor’s + // definition. [Note: if the constructor’s class contains a member + // with the same name as a direct or virtual base class of the + // class, a mem-initializer-id naming the member or base class and + // composed of a single identifier refers to the class member. A + // mem-initializer-id for the hidden base class may be specified + // using a qualified name. ] + // Look for a member, first. + CXXFieldDecl *Member = ClassDecl->getMember(MemberOrBase); + + // FIXME: Handle members of an anonymous union. + + if (Member) { + // FIXME: Perform direct initialization of the member. + return new CXXBaseOrMemberInitializer(Member, (Expr **)Args, NumArgs); + } + + // It didn't name a member, so see if it names a class. + TypeTy *BaseTy = isTypeName(*MemberOrBase, S); + if (!BaseTy) + return Diag(IdLoc, diag::err_mem_init_not_member_or_class, + MemberOrBase->getName(), SourceRange(IdLoc, RParenLoc)); + + QualType BaseType = Context.getTypeDeclType((TypeDecl *)BaseTy); + if (!BaseType->isRecordType()) + return Diag(IdLoc, diag::err_base_init_does_not_name_class, + BaseType.getAsString(), SourceRange(IdLoc, RParenLoc)); + + // C++ [class.base.init]p2: + // [...] Unless the mem-initializer-id names a nonstatic data + // member of the constructor’s class or a direct or virtual base + // of that class, the mem-initializer is ill-formed. A + // mem-initializer-list can initialize a base class using any + // name that denotes that base class type. + + // First, check for a direct base class. + const CXXBaseSpecifier *DirectBaseSpec = 0; + for (CXXRecordDecl::base_class_const_iterator Base = ClassDecl->bases_begin(); + Base != ClassDecl->bases_end(); ++Base) { + if (Context.getCanonicalType(BaseType).getUnqualifiedType() == + Context.getCanonicalType(Base->getType()).getUnqualifiedType()) { + // We found a direct base of this type. That's what we're + // initializing. + DirectBaseSpec = &*Base; + break; + } + } + + // Check for a virtual base class. + // FIXME: We might be able to short-circuit this if we know in + // advance that there are no virtual bases. + const CXXBaseSpecifier *VirtualBaseSpec = 0; + if (!DirectBaseSpec || !DirectBaseSpec->isVirtual()) { + // We haven't found a base yet; search the class hierarchy for a + // virtual base class. + BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + if (IsDerivedFrom(Context.getTypeDeclType(ClassDecl), BaseType, Paths)) { + for (BasePaths::paths_iterator Path = Paths.begin(); + Path != Paths.end(); ++Path) { + if (Path->back().Base->isVirtual()) { + VirtualBaseSpec = Path->back().Base; + break; + } + } + } + } + + // C++ [base.class.init]p2: + // If a mem-initializer-id is ambiguous because it designates both + // a direct non-virtual base class and an inherited virtual base + // class, the mem-initializer is ill-formed. + if (DirectBaseSpec && VirtualBaseSpec) + return Diag(IdLoc, diag::err_base_init_direct_and_virtual, + MemberOrBase->getName(), SourceRange(IdLoc, RParenLoc)); + + return new CXXBaseOrMemberInitializer(BaseType, (Expr **)Args, NumArgs); +} + + void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, DeclTy *TagDecl, SourceLocation LBrac, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 22d712782ca8..26fdb24e58a3 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -546,7 +546,7 @@ Sema::TryDirectInitialization(Expr *SrcExpr, QualType DestType) return TryCopyInitialization(SrcExpr, DestType); } - // Not enough support for the rest yet, actually. + // FIXME: Not enough support for the rest yet, actually. ImplicitConversionSequence ICS; ICS.ConversionKind = ImplicitConversionSequence::BadConversion; return ICS; diff --git a/clang/test/SemaCXX/constructor-initializer.cpp b/clang/test/SemaCXX/constructor-initializer.cpp new file mode 100644 index 000000000000..6b450b097ac7 --- /dev/null +++ b/clang/test/SemaCXX/constructor-initializer.cpp @@ -0,0 +1,43 @@ +// RUN: clang -fsyntax-only -verify %s +class A { + int m; +}; + +class B : public A { +public: + B() : A(), m(1), n(3.14) { } + +private: + int m; + float n; +}; + + +class C : public virtual B { +public: + C() : B() { } +}; + +class D : public C { +public: + D() : B(), C() { } +}; + +class E : public D, public B { +public: + E() : B(), D() { } // expected-error{{base class initializer 'B' names both a direct base class and an inherited virtual base class}} +}; + + +typedef int INT; + +class F : public B { +public: + int B; + + F() : B(17), + m(17), // expected-error{{member initializer 'm' does not name a non-static data member or base class}} + INT(17) // expected-error{{constructor initializer 'INT' does not name a class}} + { + } +}; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index da949106ae0e..f52f3930f07d 100644 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -464,6 +464,7 @@ welcome!

+     5.2.9 [expr.static.cast] @@ -616,7 +617,14 @@ welcome!

  12.5 [class.free]   12.6 [class.init]     12.6.1 [class.expl.init] -    12.6.2 [class.base.init] + +     12.6.2 [class.base.init] + + + + + No actual direct initialization; implicit initialization not checked. +   12.7 [class.cdtor]   12.8 [class.copy] 13 [over]