]> granicus.if.org Git - clang/commitdiff
Implement support for C++ direct initializers in declarations, e.g. "int x(1);".
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Mon, 6 Oct 2008 17:10:33 +0000 (17:10 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Mon, 6 Oct 2008 17:10:33 +0000 (17:10 +0000)
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
include/clang/Parse/Action.h
include/clang/Parse/DeclSpec.h
lib/Parse/ParseDecl.cpp
lib/Parse/Parser.cpp
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
test/SemaCXX/direct-initializer.cpp [new file with mode: 0644]

index fe5e1400cafc649a033ad866e89b73b488cd5d39..323fe2a5ea2ae41ddc6634901e3766ad0b2d3a48 100644 (file)
@@ -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.
index dc15ec2f760823353ccb32e9c5243f39ac2c3d09..ee7b7a938975054b9a1eef2fa2576ad71525bfeb 100644 (file)
@@ -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 --------------------------===//
   
index bac88bf9c2c334dd730295fdeb1e3ecf6d15b36b..cc20e21a1eb9b12be2974e1a2b4afd2dfe1fe173 100644 (file)
@@ -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
index 3017da4821333c0e24f51a621e67110ec4879d59..7d1a9bb4b653d5280960b2f658ce104832eec041 100644 (file)
@@ -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());
     
index 800be8dd0139d9eacd0cedb15f88d0f489484477..fa4bdfafb74b4b9e125b7e7ab8ff95602dae8358 100644 (file)
@@ -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() {}
index 36f5de8bb322de0d8aa3dc223fbebb4dd8b1cf3f..406d9f7e7e17fe2bd5a61b318ac78d108b8cee6f 100644 (file)
@@ -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,
index c46954568f01283eb6514a2bde3b7fcce04f8997..9d99494532b4a1b6b601fa3771a74709e92a3cc1 100644 (file)
@@ -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 (file)
index 0000000..bb0aab6
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: clang -fsyntax-only %s 
+
+int x(1);
+
+void f() {
+  int x(1);
+  for (int x(1);;) {}
+}