From 73a0d889eda0f000a441319e39540509175b8e81 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 6 Oct 2008 17:10:33 +0000 Subject: [PATCH] Implement support for C++ direct initializers in declarations, e.g. "int x(1);". This is how this kind of initializers appear in the AST: -The Init expression of the VarDecl is a functional type construction (of the VarDecl's type). -The new VarDecl::hasCXXDirectInitializer() returns true. e.g, for "int x(1);": -VarDecl 'x' has Init with expression "int(1)" (CXXFunctionalCastExpr). -hasCXXDirectInitializer() of VarDecl 'x' returns true. A major benefit is that clients that don't particularly care about which exactly form was the initializer can handle both cases without special case code. Note that codegening works now for "int x(1);" without any changes to CodeGen. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@57178 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 22 +++++++- include/clang/Parse/Action.h | 11 ++++ include/clang/Parse/DeclSpec.h | 21 ++++++-- lib/Parse/ParseDecl.cpp | 34 ++++++++++++ lib/Parse/Parser.cpp | 4 +- lib/Sema/Sema.h | 9 ++++ lib/Sema/SemaDeclCXX.cpp | 80 +++++++++++++++++++++++++++++ test/SemaCXX/direct-initializer.cpp | 8 +++ 8 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 test/SemaCXX/direct-initializer.cpp 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 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(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(ExprTys[i]); + return; + } + + VarDecl *VDecl = dyn_cast(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);;) {} +}