]> granicus.if.org Git - clang/commitdiff
Correct the order in which we cope with end-of-class-definition
authorDouglas Gregor <dgregor@apple.com>
Wed, 24 Dec 2008 00:01:03 +0000 (00:01 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 24 Dec 2008 00:01:03 +0000 (00:01 +0000)
semantics and improve our handling of default arguments. Specifically,
we follow this order:

  - As soon as the see the '}' in the class definition, the class is
  complete and we add any implicit declarations (default constructor,
  copy constructor, etc.) to the class.
  - If there are any default function arguments, parse them
  - If there were any inline member function definitions, parse them

As part of this change, we now keep track of the the fact that we've
seen unparsed default function arguments within the AST. See the new
ParmVarDecl::hasUnparsedDefaultArg member. This allows us to properly
cope with calls inside default function arguments to other functions
where we're making use of the default arguments.

Made some C++ error messages regarding failed initializations more
specific.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61406 91177308-0d34-0410-b5e6-96231b3b80d8

16 files changed:
include/clang/AST/Decl.h
include/clang/AST/ExprCXX.h
include/clang/Basic/DiagnosticKinds.def
include/clang/Parse/Action.h
lib/AST/StmtDumper.cpp
lib/AST/Type.cpp
lib/Parse/ParseDecl.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
test/SemaCXX/condition.cpp
test/SemaCXX/constructor.cpp
test/SemaCXX/copy-initialization.cpp
test/SemaCXX/default1.cpp
test/SemaCXX/default2.cpp

index 8ff8aefaaff93e9b0365c3ae267007f4e79c71a0..0401c504543dc232e3178f58b2b6835ac1baaf43 100644 (file)
@@ -504,6 +504,27 @@ public:
   Expr *getDefaultArg() { return DefaultArg; }
   void setDefaultArg(Expr *defarg) { DefaultArg = defarg; }
 
+  /// hasUnparsedDefaultArg - Determines whether this parameter has a
+  /// default argument that has not yet been parsed. This will occur
+  /// during the processing of a C++ class whose member functions have
+  /// default arguments, e.g.,
+  /// @code
+  ///   class X {
+  ///   public:
+  ///     void f(int x = 17); // x has an unparsed default argument now
+  ///   }; // x has a regular default argument now
+  /// @endcode
+  bool hasUnparsedDefaultArg() const {
+    return DefaultArg == reinterpret_cast<Expr *>(-1);
+  }
+
+  /// setUnparsedDefaultArg - Specify that this parameter has an
+  /// unparsed default argument. The argument will be replaced with a
+  /// real default argument via setDefaultArg when the class
+  /// definition enclosing the function declaration that owns this
+  /// default argument is completed.
+  void setUnparsedDefaultArg() { DefaultArg = reinterpret_cast<Expr *>(-1); }
+
   QualType getOriginalType() const;
   
   // Implement isa/cast/dyncast/etc.
index 31dfd2583340cdd37f1297a2fc16c5d979cb1f14..be33eb1939d12fdd3e9ceb5990548dc8aae18627 100644 (file)
@@ -335,7 +335,9 @@ public:
   // Param is the parameter whose default argument is used by this
   // expression.
   explicit CXXDefaultArgExpr(ParmVarDecl *param) 
-    : Expr(CXXDefaultArgExprClass, param->getDefaultArg()->getType()),
+    : Expr(CXXDefaultArgExprClass, 
+           param->hasUnparsedDefaultArg()? param->getType().getNonReferenceType()
+                                         : param->getDefaultArg()->getType()),
       Param(param) { }
 
   // Retrieve the parameter that the argument was created from.
index 7675710a34b51b77f2675d7249a13e73887628f7..321b2c69c511e612a7865dc5245acc993d515465 100644 (file)
@@ -1352,6 +1352,10 @@ DIAG(warn_value_always_false, WARNING,
 // FIXME: %2 is an english string here.
 DIAG(err_typecheck_convert_incompatible, ERROR,
      "incompatible type %2 %1, expected %0")
+DIAG(err_cannot_initialize_decl_noname, ERROR,
+     "cannot initialize a value of type %0 with an %select{rvalue|lvalue}1 of type %2")
+DIAG(err_cannot_initialize_decl, ERROR,
+     "cannot initialize %0 with an %select{rvalue|lvalue}1 of type %2")
 DIAG(warn_incompatible_qualified_id, WARNING,
      "incompatible type %2 %1, expected %0")
 DIAG(warn_incompatible_qualified_id_operands, WARNING,
index 39de870d12d9bc4dffd66900f148694b5beeaa29..13b3c8a2b749f474f4dc3b570ac6ec328d36cbe3 100644 (file)
@@ -734,6 +734,13 @@ public:
                                          ExprTy *defarg) {
   }
 
+  /// ActOnParamUnparsedDefaultArgument - We've seen a default
+  /// argument for a function parameter, but we can't parse it yet
+  /// because we're inside a class definition. Note that this default
+  /// argument will be parsed later.
+  virtual void ActOnParamUnparsedDefaultArgument(DeclTy *param, 
+                                                 SourceLocation EqualLoc) { }
+
   /// ActOnParamDefaultArgumentError - Parsing or semantic analysis of
   /// the default argument for the parameter param failed.
   virtual void ActOnParamDefaultArgumentError(DeclTy *param) { }
index f11536f9c2dc5ea31f4fc93df23fc882cdb7ba83..c3ce92de60b7258f5b69a389c3ff23bb48483548 100644 (file)
@@ -83,12 +83,14 @@ namespace  {
     void DumpType(QualType T) {
       fprintf(F, "'%s'", T.getAsString().c_str());
 
-      // If the type is directly a typedef, strip off typedefness to give at
-      // least one level of concreteness.
-      if (TypedefType *TDT = dyn_cast<TypedefType>(T)) {
-        QualType Simplified = 
-          TDT->LookThroughTypedefs().getQualifiedType(T.getCVRQualifiers());
-        fprintf(F, ":'%s'", Simplified.getAsString().c_str());
+      if (!T.isNull()) {
+        // If the type is directly a typedef, strip off typedefness to give at
+        // least one level of concreteness.
+        if (TypedefType *TDT = dyn_cast<TypedefType>(T)) {
+          QualType Simplified = 
+            TDT->LookThroughTypedefs().getQualifiedType(T.getCVRQualifiers());
+          fprintf(F, ":'%s'", Simplified.getAsString().c_str());
+        }
       }
     }
     void DumpStmt(const Stmt *Node) {
index 5909c976aa6aaf0ca7bd016ff3ad66c8f95c9179..c70ad4af521ef34c2dc01a45423bbc96f058bf65 100644 (file)
@@ -841,7 +841,7 @@ static void AppendTypeQualList(std::string &S, unsigned TypeQuals) {
 
 void QualType::getAsStringInternal(std::string &S) const {
   if (isNull()) {
-    S += "NULL TYPE\n";
+    S += "NULL TYPE";
     return;
   }
   
index 16e7e774aa990e628f1d054af44e91eb1e9efd4c..213a210b9eacf83341f92290717aa77e855b2163 100644 (file)
@@ -1811,6 +1811,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
       // ActOnParamDefaultArgument will reject the default argument in
       // C.
       if (Tok.is(tok::equal)) {
+        SourceLocation EqualLoc = Tok.getLocation();
+
         // Parse the default argument
         if (D.getContext() == Declarator::MemberContext) {
           // If we're inside a class definition, cache the tokens
@@ -1824,10 +1826,12 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
                                     tok::semi, false)) {
             delete DefArgToks;
             DefArgToks = 0;
-          } 
+            Actions.ActOnParamDefaultArgumentError(Param);
+          } else
+            Actions.ActOnParamUnparsedDefaultArgument(Param, EqualLoc);
         } else {
           // Consume the '='.
-          SourceLocation EqualLoc = ConsumeToken();
+          ConsumeToken();
         
           OwningExprResult DefArgResult(ParseAssignmentExpression());
           if (DefArgResult.isInvalid()) {
index 2664c57ee4bba1de979a64690aad0405965de8a9..843e732e8737a2be4fde5761aa37a6ec843dc278 100644 (file)
@@ -276,6 +276,8 @@ public:
   virtual void ActOnParamDefaultArgument(DeclTy *param, 
                                          SourceLocation EqualLoc,
                                          ExprTy *defarg);
+  virtual void ActOnParamUnparsedDefaultArgument(DeclTy *param, 
+                                                 SourceLocation EqualLoc);
   virtual void ActOnParamDefaultArgumentError(DeclTy *param);
   void AddInitializerToDecl(DeclTy *dcl, ExprArg init);
   void ActOnUninitializedDecl(DeclTy *dcl);
index d84d6adad0120bb1dc3c5c9487a8dac3b8be7d16..c6689591769891f351a752ecd73e00a935aaed0c 100644 (file)
@@ -888,9 +888,14 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType,
       if (!PerformImplicitConversion(Init, DeclType, "initializing"))
         return false;
       
-      return Diag(InitLoc, diag::err_typecheck_convert_incompatible)
-        << DeclType << InitEntity << "initializing"
-        << Init->getSourceRange();
+      if (InitEntity)
+        return Diag(InitLoc, diag::err_cannot_initialize_decl)
+          << InitEntity << (int)(Init->isLvalue(Context) == Expr::LV_Valid)
+          << Init->getType() << Init->getSourceRange();
+      else
+        return Diag(InitLoc, diag::err_cannot_initialize_decl_noname)
+          << DeclType << (int)(Init->isLvalue(Context) == Expr::LV_Valid)
+          << Init->getType() << Init->getSourceRange();
     }
 
     // C99 6.7.8p16.
index 918fd2ac9a78c312633849a2f220177462ea2747..48373369bcfcd508b36c6c0c50719f964f23e8e5 100644 (file)
@@ -126,8 +126,9 @@ Sema::ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc,
   //   a declaration of a variable of the parameter type, using the
   //   copy-initialization semantics (8.5).
   Expr *DefaultArgPtr = DefaultArg.get();
-  bool DefaultInitFailed = PerformCopyInitialization(DefaultArgPtr, ParamType,
-                                                     "in default argument");
+  bool DefaultInitFailed = CheckInitializerTypes(DefaultArgPtr, ParamType,
+                                                 EqualLoc,
+                                                 Param->getDeclName());
   if (DefaultArgPtr != DefaultArg.get()) {
     DefaultArg.take();
     DefaultArg.reset(DefaultArgPtr);
@@ -147,6 +148,17 @@ Sema::ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc,
   Param->setDefaultArg(DefaultArg.take());
 }
 
+/// ActOnParamUnparsedDefaultArgument - We've seen a default
+/// argument for a function parameter, but we can't parse it yet
+/// because we're inside a class definition. Note that this default
+/// argument will be parsed later.
+void Sema::ActOnParamUnparsedDefaultArgument(DeclTy *param, 
+                                             SourceLocation EqualLoc) {
+  ParmVarDecl *Param = (ParmVarDecl*)param;
+  if (Param)
+    Param->setUnparsedDefaultArg();
+}
+
 /// ActOnParamDefaultArgumentError - Parsing or semantic analysis of
 /// the default argument for the parameter param failed.
 void Sema::ActOnParamDefaultArgumentError(DeclTy *param) {
@@ -171,16 +183,16 @@ void Sema::CheckExtraCXXDefaultArguments(Declarator &D) {
     if (chunk.Kind == DeclaratorChunk::Function) {
       for (unsigned argIdx = 0; argIdx < chunk.Fun.NumArgs; ++argIdx) {
         ParmVarDecl *Param = (ParmVarDecl *)chunk.Fun.ArgInfo[argIdx].Param;
-        if (Param->getDefaultArg()) {
-          Diag(Param->getLocation(), diag::err_param_default_argument_nonfunc)
-            << Param->getDefaultArg()->getSourceRange();
-          Param->setDefaultArg(0);
-        } else if (CachedTokens *Toks 
-                     = chunk.Fun.ArgInfo[argIdx].DefaultArgTokens) {
+        if (Param->hasUnparsedDefaultArg()) {
+          CachedTokens *Toks = chunk.Fun.ArgInfo[argIdx].DefaultArgTokens;
           Diag(Param->getLocation(), diag::err_param_default_argument_nonfunc)
             << SourceRange((*Toks)[1].getLocation(), Toks->back().getLocation());
           delete Toks;
           chunk.Fun.ArgInfo[argIdx].DefaultArgTokens = 0;
+        } else if (Param->getDefaultArg()) {
+          Diag(Param->getLocation(), diag::err_param_default_argument_nonfunc)
+            << Param->getDefaultArg()->getSourceRange();
+          Param->setDefaultArg(0);
         }
       }
     }
@@ -269,7 +281,8 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) {
     for (p = 0; p <= LastMissingDefaultArg; ++p) {
       ParmVarDecl *Param = FD->getParamDecl(p);
       if (Param->getDefaultArg()) {
-        delete Param->getDefaultArg();
+        if (!Param->hasUnparsedDefaultArg())
+          Param->getDefaultArg()->Destroy(Context);
         Param->setDefaultArg(0);
       }
     }
@@ -736,6 +749,7 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
   ActOnFields(S, RLoc, TagDecl,
               (DeclTy**)FieldCollector->getCurFields(),
               FieldCollector->getCurNumFields(), LBrac, RBrac, 0);
+  AddImplicitlyDeclaredMembersToClass(cast<CXXRecordDecl>((Decl*)TagDecl));
 }
 
 /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
@@ -872,7 +886,6 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
 void Sema::ActOnFinishCXXClassDef(DeclTy *D) {
   CXXRecordDecl *Rec = cast<CXXRecordDecl>(static_cast<Decl *>(D));
   FieldCollector->FinishClass();
-  AddImplicitlyDeclaredMembersToClass(Rec);
   PopDeclContext();
 
   // Everything, including inline method definitions, have been parsed.
@@ -901,6 +914,12 @@ void Sema::ActOnStartDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method) {
 /// ActOnParamDefaultArgument event for this parameter.
 void Sema::ActOnDelayedCXXMethodParameter(Scope *S, DeclTy *ParamD) {
   ParmVarDecl *Param = (ParmVarDecl*)ParamD;
+
+  // If this parameter has an unparsed default argument, clear it out
+  // to make way for the parsed default argument.
+  if (Param->hasUnparsedDefaultArg())
+    Param->setDefaultArg(0);
+
   S->AddDecl(Param);
   if (Param->getDeclName())
     IdResolver.AddDecl(Param);
@@ -1871,7 +1890,11 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
   if (Op != OO_Call) {
     for (FunctionDecl::param_iterator Param = FnDecl->param_begin();
          Param != FnDecl->param_end(); ++Param) {
-      if (Expr *DefArg = (*Param)->getDefaultArg())
+      if ((*Param)->hasUnparsedDefaultArg())
+        return Diag((*Param)->getLocation(), 
+                    diag::err_operator_overload_default_arg)
+          << FnDecl->getDeclName();
+      else if (Expr *DefArg = (*Param)->getDefaultArg())
         return Diag((*Param)->getLocation(),
                     diag::err_operator_overload_default_arg)
           << FnDecl->getDeclName() << DefArg->getSourceRange();
index 4088f443f75f0737dbc98edc1159af88feb5d611..d65c10bb50c0f8f21a65d89a4c625c9ba301db83 100644 (file)
@@ -1507,16 +1507,17 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
     QualType ProtoArgType = Proto->getArgType(i);
     
     Expr *Arg;
-    if (i < NumArgs) 
+    if (i < NumArgs) {
       Arg = Args[i];
-    else 
+
+      // Pass the argument.
+      if (PerformCopyInitialization(Arg, ProtoArgType, "passing"))
+        return true;
+    } else 
+      // We already type-checked the argument, so we know it works.
       Arg = new CXXDefaultArgExpr(FDecl->getParamDecl(i));
     QualType ArgType = Arg->getType();
-    
-    // Pass the argument.
-    if (PerformCopyInitialization(Arg, ProtoArgType, "passing"))
-      return true;
-    
+        
     Call->setArg(i, Arg);
   }
   
index d611364cf03cc5fbc3d6502fb40f4b447aef1b8c..43117e61c38fc5117dec71fe03c1851425e4d0e1 100644 (file)
@@ -16,8 +16,8 @@ void test() {
   for (;s;) ; // expected-error {{expression must have bool type (or be convertible to bool) ('struct S' invalid)}}
   switch (s) {} // expected-error {{statement requires expression of integer type ('struct S' invalid)}}
 
-  while (struct S {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{incompatible type}} expected-error {{expression must have bool type}}
-  while (struct {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{incompatible type}} expected-error {{expression must have bool type}}
+  while (struct S {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize 'x' with an rvalue of type 'int'}} expected-error {{expression must have bool type}}
+  while (struct {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize 'x' with an rvalue of type 'int'}} expected-error {{expression must have bool type}}
   switch (enum {E} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{incompatible type}}
 
   if (int x=0) { // expected-note {{previous definition is here}}
index 536593d5120a53691b12e652e544aa173bf98d9a..68099ecac962221d8c9aab70c32faa19822d3246 100644 (file)
@@ -1,5 +1,4 @@
 // RUN: clang -fsyntax-only -verify %s 
-
 typedef int INT;
 
 class Foo {
@@ -37,3 +36,7 @@ struct y {
   y(int);
 };
 extern y b;
+
+struct Length {
+  Length l() const { return *this; }
+};
index 5ef84de9acda8a231bd6d6ab3158608edc64430d..e380cc1ad3c753cd98731d01e8cadb7deee6759e 100644 (file)
@@ -13,5 +13,5 @@ void f(Y y, int *ip, float *fp) {
   X x1 = y; // expected-error{{no matching constructor for initialization of 'x1'; candidate is:}}
   X x2 = 0;
   X x3 = ip;
-  X x4 = fp; // expected-error{{incompatible type initializing 'x4', expected 'class X'}}
+  X x4 = fp; // expected-error{{cannot initialize 'x4' with an lvalue of type 'float *'}}
 }
index 286be6106b118a43137ccf41b096f5d35bdedf6f..28444207d86666cc80c04ade82d64cde5df28507 100644 (file)
@@ -26,4 +26,6 @@ struct Y {
   explicit Y(int);
 };
 
-void k(Y y = 17); // expected-error{{incompatible type in default argument}}
+void k(Y y = 17); // expected-error{{cannot initialize 'y' with an rvalue of type 'int'}}
+
+void kk(Y = 17); // expected-error{{cannot initialize a value of type 'struct Y' with an rvalue of type 'int'}}
index 863ac0e25fd0a3f89bdeb36f0f1a3347ae9bc161..c2873af3f08c5380e3dcce618f36e333c09f8b4e 100644 (file)
@@ -103,8 +103,20 @@ public:
     Z z2;    // expected-error{{no matching constructor for initialization}}
     Z z3(z);
   }
+
+  void test_Z(const Z& z) {
+    Z z2(z); // expected-error{{no matching constructor for initialization of 'z2'}}
+  }
 };
 
 void test_Z(const Z& z) {
   Z z2(z); // expected-error{{no matching constructor for initialization of 'z2'}}
 }
+
+struct ZZ {
+  void f(ZZ z = g()); // expected-error{{no matching constructor for initialization}}
+
+  static ZZ g(int = 17);
+
+  ZZ(ZZ&, int = 17); // expected-note{{candidate function}}
+};