diff options
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 33 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseCXXInlineMethods.cpp | 22 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 97 | ||||
| -rw-r--r-- | clang/lib/Parse/Parser.cpp | 21 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 9 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 106 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 2 |
7 files changed, 279 insertions, 11 deletions
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 21701461e61..9f20ac81639 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<uintptr_t>(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<uintptr_t>(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 ca0003440fe..7d977c1251b 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 7d03c591139..57fa193e756 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<MemInitTy*, 4> 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 6beaac0151f..625da6c281e 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 ce70855a378..72b56deca37 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 e4321f53ff0..95d17fa327b 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<CXXConstructorDecl>((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 22d712782ca..26fdb24e58a 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; |

