diff options
-rw-r--r-- | include/clang/AST/Decl.h | 22 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 11 | ||||
-rw-r--r-- | include/clang/Parse/DeclSpec.h | 21 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 34 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 9 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 80 | ||||
-rw-r--r-- | test/SemaCXX/direct-initializer.cpp | 8 |
8 files changed, 184 insertions, 5 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index fe5e1400ca..323fe2a5ea 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -229,6 +229,7 @@ private: // FIXME: This can be packed into the bitfields in Decl. unsigned SClass : 3; bool ThreadSpecified : 1; + bool HasCXXDirectInit : 1; // Move to DeclGroup when it is implemented. SourceLocation TypeSpecStartLoc; @@ -238,7 +239,8 @@ protected: QualType T, StorageClass SC, ScopedDecl *PrevDecl, SourceLocation TSSL = SourceLocation()) : ValueDecl(DK, DC, L, Id, T, PrevDecl), Init(0), - ThreadSpecified(false), TypeSpecStartLoc(TSSL) { SClass = SC; } + ThreadSpecified(false), HasCXXDirectInit(false), + TypeSpecStartLoc(TSSL) { SClass = SC; } public: static VarDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, @@ -257,6 +259,24 @@ public: bool isThreadSpecified() const { return ThreadSpecified; } + + void setCXXDirectInitializer(bool T) { HasCXXDirectInit = T; } + + /// hasCXXDirectInitializer - If true, the initializer was a direct + /// initializer, e.g: "int x(1);". The Init expression will be an expression + /// that constructs the type with functional notation, e.g. for: + /// + /// int x(1); + /// + /// hasCXXDirectInitializer will be true, + /// Init expression will be a "int(1)" functional-cast expression. + /// + /// Clients can distinguish between "int x(1);" and "int x = int(1);" by + /// checking hasCXXDirectInitializer. + /// + bool hasCXXDirectInitializer() const { + return HasCXXDirectInit; + } /// hasLocalStorage - Returns true if a variable with function scope /// is a non-static local variable. diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index dc15ec2f76..ee7b7a9389 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -571,6 +571,17 @@ public: SourceLocation EqualLoc, ExprTy *defarg) { } + + /// AddCXXDirectInitializerToDecl - This action is called immediately after + /// ActOnDeclarator, when a C++ direct initializer is present. + /// e.g: "int x(1);" + virtual void AddCXXDirectInitializerToDecl(DeclTy *Dcl, + SourceLocation LParenLoc, + ExprTy **Exprs, unsigned NumExprs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + return; + } //===------------------------- C++ Expressions --------------------------===// diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index bac88bf9c2..cc20e21a1e 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -595,7 +595,10 @@ private: llvm::SmallVector<DeclaratorChunk, 8> DeclTypeInfo; // InvalidType - Set by Sema::GetTypeForDeclarator(). - bool InvalidType; + bool InvalidType : 1; + + /// GroupingParens - Set by Parser::ParseParenDeclarator(). + bool GroupingParens : 1; /// AttrList - Attributes. AttributeList *AttrList; @@ -605,8 +608,8 @@ private: public: Declarator(const DeclSpec &ds, TheContext C) - : DS(ds), Identifier(0), Context(C), InvalidType(false), AttrList(0), - AsmLabel(0) { + : DS(ds), Identifier(0), Context(C), InvalidType(false), + GroupingParens(false), AttrList(0), AsmLabel(0) { } ~Declarator() { @@ -666,6 +669,15 @@ public: bool mayHaveIdentifier() const { return Context != TypeNameContext; } + + /// mayBeFollowedByCXXDirectInit - Return true if the declarator can be + /// followed by a C++ direct initializer, e.g. "int x(1);". + bool mayBeFollowedByCXXDirectInit() const { + return !hasGroupingParens() && + (Context == FileContext || + Context == BlockContext || + Context == ForContext ); + } /// isPastIdentifier - Return true if we have parsed beyond the point where /// the @@ -725,6 +737,9 @@ public: void setInvalidType(bool flag) { InvalidType = flag; } bool getInvalidType() const { return InvalidType; } + + void setGroupingParens(bool flag) { GroupingParens = flag; } + bool hasGroupingParens() const { return GroupingParens; } }; /// FieldDeclarator - This little struct is used to capture information about diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 3017da4821..7d1a9bb4b6 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -245,6 +245,11 @@ Parser::DeclTy *Parser::ParseSimpleDeclaration(unsigned Context) { /// declarator '=' initializer /// [GNU] declarator simple-asm-expr[opt] attributes[opt] /// [GNU] declarator simple-asm-expr[opt] attributes[opt] '=' initializer +/// [C++] declarator initializer[opt] +/// +/// [C++] initializer: +/// [C++] '=' initializer-clause +/// [C++] '(' expression-list ')' /// Parser::DeclTy *Parser:: ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) { @@ -284,6 +289,27 @@ ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) { return 0; } Actions.AddInitializerToDecl(LastDeclInGroup, Init.Val); + } else if (Tok.is(tok::l_paren)) { + // Parse C++ direct initializer: '(' expression-list ')' + SourceLocation LParenLoc = ConsumeParen(); + ExprListTy Exprs; + CommaLocsTy CommaLocs; + + bool InvalidExpr = false; + if (ParseExpressionList(Exprs, CommaLocs)) { + SkipUntil(tok::r_paren); + InvalidExpr = true; + } + // Match the ')'. + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + if (!InvalidExpr) { + assert(!Exprs.empty() && Exprs.size()-1 == CommaLocs.size() && + "Unexpected number of commas!"); + Actions.AddCXXDirectInitializerToDecl(LastDeclInGroup, LParenLoc, + &Exprs[0], Exprs.size(), + &CommaLocs[0], RParenLoc); + } } // If we don't have a comma, it is either the end of the list (a ';') or an @@ -1200,6 +1226,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { while (1) { if (Tok.is(tok::l_paren)) { + // The paren may be part of a C++ direct initializer, eg. "int x(1);". + // In such a case, check if we actually have a function declarator; if it + // is not, the declarator has been fully parsed. + if (getLang().CPlusPlus && D.mayBeFollowedByCXXDirectInit() && + !isCXXFunctionDeclarator()) + break; ParseFunctionDeclarator(ConsumeParen(), D); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); @@ -1247,6 +1279,8 @@ void Parser::ParseParenDeclarator(Declarator &D) { // direct-declarator: '(' declarator ')' // direct-declarator: '(' attributes declarator ')' if (isGrouping) { + D.setGroupingParens(true); + if (Tok.is(tok::kw___attribute)) D.AddAttributes(ParseAttributes()); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 800be8dd01..fa4bdfafb7 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -449,7 +449,9 @@ Parser::DeclTy *Parser::ParseDeclarationOrFunctionDefinition() { Tok.is(tok::comma) || // int X(), -> not a function def Tok.is(tok::semi) || // int X(); -> not a function def Tok.is(tok::kw_asm) || // int X() __asm__ -> not a function def - Tok.is(tok::kw___attribute)) { // int X() __attr__ -> not a function def + Tok.is(tok::kw___attribute) || // int X() __attr__ -> not a function def + (getLang().CPlusPlus && + Tok.is(tok::l_paren)) ) { // int X(0) -> not a function def [C++] // FALL THROUGH. } else if (DeclaratorInfo.isFunctionDeclarator() && (Tok.is(tok::l_brace) || // int X() {} diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 36f5de8bb3..406d9f7e7e 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -593,6 +593,15 @@ public: SourceLocation LBrace); virtual void ActOnFinishNamespaceDef(DeclTy *Dcl, SourceLocation RBrace); + /// AddCXXDirectInitializerToDecl - This action is called immediately after + /// ActOnDeclarator, when a C++ direct initializer is present. + /// e.g: "int x(1);" + virtual void AddCXXDirectInitializerToDecl(DeclTy *Dcl, + SourceLocation LParenLoc, + ExprTy **Exprs, unsigned NumExprs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc); + /// ActOnCXXCasts - Parse {dynamic,static,reinterpret,const}_cast's. virtual ExprResult ActOnCXXCasts(SourceLocation OpLoc, tok::TokenKind Kind, SourceLocation LAngleBracketLoc, TypeTy *Ty, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index c46954568f..9d99494532 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -548,3 +548,83 @@ void Sema::ActOnFinishNamespaceDef(DeclTy *D, SourceLocation RBrace) { Namespc->setRBracLoc(RBrace); PopDeclContext(); } + + +/// AddCXXDirectInitializerToDecl - This action is called immediately after +/// ActOnDeclarator, when a C++ direct initializer is present. +/// e.g: "int x(1);" +void Sema::AddCXXDirectInitializerToDecl(DeclTy *Dcl, SourceLocation LParenLoc, + ExprTy **ExprTys, unsigned NumExprs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + Decl *RealDecl = static_cast<Decl *>(Dcl); + assert(NumExprs != 0 && ExprTys && "missing expressions"); + + // If there is no declaration, there was an error parsing it. Just ignore + // the initializer. + if (RealDecl == 0) { + for (int i=0; i != NumExprs; ++i) + delete static_cast<Expr *>(ExprTys[i]); + return; + } + + VarDecl *VDecl = dyn_cast<VarDecl>(RealDecl); + if (!VDecl) { + Diag(RealDecl->getLocation(), diag::err_illegal_initializer); + RealDecl->setInvalidDecl(); + return; + } + + // We will treat direct-initialization as a copy-initialization with a + // type-construction expression of the variable's type. In plain english: + // We will treat: + // int x(1); -as-> int x = int(1); + // and for class types: + // ClassType x(a,b,c); -as-> ClassType x = ClassType(a,b,c); + // + // Clients that want to distinguish between the two forms, can check for + // direct initializer using VarDecl::hasCXXDirectInitializer(). + // A major benefit is that clients that don't particularly care about which + // exactly form was it (like the CodeGen) can handle both cases without + // special case code. + // + // According to the C++ standard, there shouldn't be semantic differences + // between a direct-initialization and a copy-initialization where the + // destination type is the same as the source type: + // + // C++ 8.5p11: + // The form of initialization (using parentheses or '=') is generally + // insignificant, but does matter when the entity being initialized has a + // class type; see below. + // C++ 8.5p15: + // [...] + // If the initialization is direct-initialization, or if it is + // copy-initialization where the cv-unqualified version of the source type is + // the same class as, or a derived class of, the class of the destination, + // constructors are considered. The applicable constructors are enumerated + // (13.3.1.3), and the best one is chosen through overload resolution (13.3). + // The constructor so selected is called to initialize the object, with the + // initializer expression(s) as its argument(s). If no constructor applies, or + // the overload resolution is ambiguous, the initialization is ill-formed. + // [...] + // + // Note that according to C++ 8.5p15, the same semantic process is applied + // to both the direct-initialization and copy-initialization, + // if destination type == source type. + + // Get an expression for constructing the type of the variable, using the + // expression list of the initializer. + ExprResult Res = ActOnCXXTypeConstructExpr(VDecl->getLocation(), + VDecl->getType().getAsOpaquePtr(), + LParenLoc, ExprTys, NumExprs, + CommaLocs, RParenLoc); + if (Res.isInvalid) { + RealDecl->setInvalidDecl(); + return; + } + + // Performs additional semantic checks. + AddInitializerToDecl(Dcl, Res.Val); + // Let clients know that initialization was done with a direct initializer. + VDecl->setCXXDirectInitializer(true); +} diff --git a/test/SemaCXX/direct-initializer.cpp b/test/SemaCXX/direct-initializer.cpp new file mode 100644 index 0000000000..bb0aab6500 --- /dev/null +++ b/test/SemaCXX/direct-initializer.cpp @@ -0,0 +1,8 @@ +// RUN: clang -fsyntax-only %s + +int x(1); + +void f() { + int x(1); + for (int x(1);;) {} +} |