]> granicus.if.org Git - clang/commitdiff
Only instantiate a default argument once.
authorJohn McCall <rjmccall@apple.com>
Wed, 6 Jan 2016 22:34:54 +0000 (22:34 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 6 Jan 2016 22:34:54 +0000 (22:34 +0000)
By storing the instantiated expression back in the ParmVarDecl,
we remove the last need for separately storing the sub-expression
of a CXXDefaultArgExpr.  This makes PCH/Modules merging quite
simple: CXXDefaultArgExpr records are serialized as references
to the ParmVarDecl, and we ignore redundant attempts to overwrite
the instantiated expression.

This has some extremely marginal impact on user-facing semantics.
However, the major effect is that it avoids IRGen errors about
conflicting definitions due to lambdas in the argument being
instantiated multiple times while sharing the same mangling.
It should also slightly improve memory usage and module file size.

rdar://23810407

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

14 files changed:
include/clang/AST/ASTMutationListener.h
include/clang/AST/ExprCXX.h
include/clang/Serialization/ASTWriter.h
lib/AST/ExprCXX.cpp
lib/Frontend/MultiplexConsumer.cpp
lib/Sema/SemaExpr.cpp
lib/Serialization/ASTCommon.h
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTReaderStmt.cpp
lib/Serialization/ASTWriter.cpp
lib/Serialization/ASTWriterStmt.cpp
test/PCH/chain-default-argument-instantiation.cpp [new file with mode: 0644]
test/SemaCXX/conversion.cpp
test/SemaTemplate/default-arguments-cxx0x.cpp

index 3ff392de11a78e31143279aae2ff09566d202ea3..cf3b55d7b2c748461daea4831338754bf4ac4088 100644 (file)
@@ -29,6 +29,7 @@ namespace clang {
   class ObjCContainerDecl;
   class ObjCInterfaceDecl;
   class ObjCPropertyDecl;
+  class ParmVarDecl;
   class QualType;
   class RecordDecl;
   class TagDecl;
@@ -88,6 +89,9 @@ public:
   /// \brief A function template's definition was instantiated.
   virtual void FunctionDefinitionInstantiated(const FunctionDecl *D) {}
 
+  /// \brief A default argument was instantiated.
+  virtual void DefaultArgumentInstantiated(const ParmVarDecl *D) {}
+
   /// \brief A new objc category class was added for an interface.
   virtual void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
                                             const ObjCInterfaceDecl *IFD) {}
index 0608abac1fe9c272d2e5f84dbc75527a0e952971..6821274986479509f53b8c0e8087ce95c4d2c48f 100644 (file)
@@ -951,15 +951,9 @@ public:
 /// This wraps up a function call argument that was created from the
 /// corresponding parameter's default argument, when the call did not
 /// explicitly supply arguments for all of the parameters.
-class CXXDefaultArgExpr final
-    : public Expr,
-      private llvm::TrailingObjects<CXXDefaultArgExpr, Expr *> {
+class CXXDefaultArgExpr final : public Expr {
   /// \brief The parameter whose default is being used.
-  ///
-  /// When the bit is set, the subexpression is stored after the
-  /// CXXDefaultArgExpr itself. When the bit is clear, the parameter's
-  /// actual default expression is the subexpression.
-  llvm::PointerIntPair<ParmVarDecl *, 1, bool> Param;
+  ParmVarDecl *Param;
 
   /// \brief The location where the default argument expression was used.
   SourceLocation Loc;
@@ -971,16 +965,7 @@ class CXXDefaultArgExpr final
              : param->getDefaultArg()->getType(),
            param->getDefaultArg()->getValueKind(),
            param->getDefaultArg()->getObjectKind(), false, false, false, false),
-      Param(param, false), Loc(Loc) { }
-
-  CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *param,
-                    Expr *SubExpr)
-    : Expr(SC, SubExpr->getType(),
-           SubExpr->getValueKind(), SubExpr->getObjectKind(),
-           false, false, false, false),
-      Param(param, true), Loc(Loc) {
-    *getTrailingObjects<Expr *>() = SubExpr;
-  }
+      Param(param), Loc(Loc) { }
 
 public:
   CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {}
@@ -992,24 +977,15 @@ public:
     return new (C) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param);
   }
 
-  // \p Param is the parameter whose default argument is used by this
-  // expression, and \p SubExpr is the expression that will actually be used.
-  static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc,
-                                   ParmVarDecl *Param, Expr *SubExpr);
-
   // Retrieve the parameter that the argument was created from.
-  const ParmVarDecl *getParam() const { return Param.getPointer(); }
-  ParmVarDecl *getParam() { return Param.getPointer(); }
+  const ParmVarDecl *getParam() const { return Param; }
+  ParmVarDecl *getParam() { return Param; }
 
   // Retrieve the actual argument to the function call.
   const Expr *getExpr() const {
-    if (Param.getInt())
-      return *getTrailingObjects<Expr *>();
     return getParam()->getDefaultArg();
   }
   Expr *getExpr() {
-    if (Param.getInt())
-      return *getTrailingObjects<Expr *>();
     return getParam()->getDefaultArg();
   }
 
@@ -1033,7 +1009,6 @@ public:
     return child_range(child_iterator(), child_iterator());
   }
 
-  friend TrailingObjects;
   friend class ASTStmtReader;
   friend class ASTStmtWriter;
 };
index ed345472fc7da41741da0c65aae3adf8e1c7016e..ef8c653413882dcd41376df26f9849d639e307f1 100644 (file)
@@ -871,6 +871,7 @@ public:
                               const FunctionDecl *Delete) override;
   void CompletedImplicitDefinition(const FunctionDecl *D) override;
   void StaticDataMemberInstantiated(const VarDecl *D) override;
+  void DefaultArgumentInstantiated(const ParmVarDecl *D) override;
   void FunctionDefinitionInstantiated(const FunctionDecl *D) override;
   void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
                                     const ObjCInterfaceDecl *IFD) override;
index d35efcb101b5aa7594674b3c11fcf4fdb55e167a..ea983340a293c5659ca1c7d94ef738433f1176ed 100644 (file)
@@ -763,14 +763,6 @@ const IdentifierInfo *UserDefinedLiteral::getUDSuffix() const {
   return cast<FunctionDecl>(getCalleeDecl())->getLiteralIdentifier();
 }
 
-CXXDefaultArgExpr *
-CXXDefaultArgExpr::Create(const ASTContext &C, SourceLocation Loc, 
-                          ParmVarDecl *Param, Expr *SubExpr) {
-  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(1));
-  return new (Mem) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, 
-                                     SubExpr);
-}
-
 CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &C, SourceLocation Loc,
                                        FieldDecl *Field, QualType T)
     : Expr(CXXDefaultInitExprClass, T.getNonLValueExprType(C),
index 12c85240bd75a3ca41f09067db9d00eef1fc50df..f8b73e9034b3eaef1ac9f7fd4a91c284e79f9f30 100644 (file)
@@ -119,6 +119,7 @@ public:
                               const FunctionDecl *Delete) override;
   void CompletedImplicitDefinition(const FunctionDecl *D) override;
   void StaticDataMemberInstantiated(const VarDecl *D) override;
+  void DefaultArgumentInstantiated(const ParmVarDecl *D) override;
   void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
                                     const ObjCInterfaceDecl *IFD) override;
   void FunctionDefinitionInstantiated(const FunctionDecl *D) override;
@@ -193,6 +194,11 @@ void MultiplexASTMutationListener::StaticDataMemberInstantiated(
   for (size_t i = 0, e = Listeners.size(); i != e; ++i)
     Listeners[i]->StaticDataMemberInstantiated(D);
 }
+void MultiplexASTMutationListener::DefaultArgumentInstantiated(
+                                                         const ParmVarDecl *D) {
+  for (size_t i = 0, e = Listeners.size(); i != e; ++i)
+    Listeners[i]->DefaultArgumentInstantiated(D);
+}
 void MultiplexASTMutationListener::AddedObjCCategoryToInterface(
                                                  const ObjCCategoryDecl *CatD,
                                                  const ObjCInterfaceDecl *IFD) {
index 5d0c6057f54fff0a39e4bbf646988f64f105ee97..f1b9987a2f0437f47a21c41a02a18bc28f4fb166 100644 (file)
@@ -4315,8 +4315,15 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
 
     Expr *Arg = Result.getAs<Expr>();
     CheckCompletedExpr(Arg, Param->getOuterLocStart());
+
+    // Remember the instantiated default argument.
+    Param->setDefaultArg(Arg);
+    if (ASTMutationListener *L = getASTMutationListener()) {
+      L->DefaultArgumentInstantiated(Param);
+    }
+
     // Build the default argument expression.
-    return CXXDefaultArgExpr::Create(Context, CallLoc, Param, Arg);
+    return CXXDefaultArgExpr::Create(Context, CallLoc, Param);
   }
 
   // If the default expression creates temporaries, we need to
index e59bc891f9b9b1c17935d6df374bbb524a0cdbea..64f583c987282d47bb3585661188eb6da03faabf 100644 (file)
@@ -29,6 +29,7 @@ enum DeclUpdateKind {
   UPD_CXX_ADDED_FUNCTION_DEFINITION,
   UPD_CXX_INSTANTIATED_STATIC_DATA_MEMBER,
   UPD_CXX_INSTANTIATED_CLASS_DEFINITION,
+  UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT,
   UPD_CXX_RESOLVED_DTOR_DELETE,
   UPD_CXX_RESOLVED_EXCEPTION_SPEC,
   UPD_CXX_DEDUCED_RETURN_TYPE,
index 8fb110e4551d08c46a70b795f5421c4cf613af16..5bf95f878d49d64bc0ec13efe501560d41e391d2 100644 (file)
@@ -3626,6 +3626,21 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
           Reader.ReadSourceLocation(ModuleFile, Record, Idx));
       break;
 
+    case UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT: {
+      auto Param = cast<ParmVarDecl>(D);
+
+      // We have to read the default argument regardless of whether we use it
+      // so that hypothetical further update records aren't messed up.
+      // TODO: Add a function to skip over the next expr record.
+      auto DefaultArg = Reader.ReadExpr(F);
+
+      // Only apply the update if the parameter still has an uninstantiated
+      // default argument.
+      if (Param->hasUninstantiatedDefaultArg())
+        Param->setDefaultArg(DefaultArg);
+      break;
+    }
+
     case UPD_CXX_ADDED_FUNCTION_DEFINITION: {
       FunctionDecl *FD = cast<FunctionDecl>(D);
       if (Reader.PendingBodies[FD]) {
index bc678aff78610d9032190ce60ee786e78fbd4ced..ad81ac844209c0b05cd9ff43bc4b1453ed7ba2d2 100644 (file)
@@ -1364,10 +1364,7 @@ void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) {
 
 void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
   VisitExpr(E);
-
-  assert((bool)Record[Idx] == E->Param.getInt() && "We messed up at creation ?");
-  ++Idx; // HasOtherExprStored and SubExpr was handled during creation.
-  E->Param.setPointer(ReadDeclAs<ParmVarDecl>(Record, Idx));
+  E->Param = ReadDeclAs<ParmVarDecl>(Record, Idx);
   E->Loc = ReadSourceLocation(Record, Idx);
 }
 
@@ -3205,16 +3202,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
     case EXPR_CXX_THROW:
       S = new (Context) CXXThrowExpr(Empty);
       break;
-    case EXPR_CXX_DEFAULT_ARG: {
-      bool HasOtherExprStored = Record[ASTStmtReader::NumExprFields];
-      if (HasOtherExprStored) {
-        Expr *SubExpr = ReadSubExpr();
-        S = CXXDefaultArgExpr::Create(Context, SourceLocation(), nullptr,
-                                      SubExpr);
-      } else
-        S = new (Context) CXXDefaultArgExpr(Empty);
+    case EXPR_CXX_DEFAULT_ARG:
+      S = new (Context) CXXDefaultArgExpr(Empty);
       break;
-    }
     case EXPR_CXX_DEFAULT_INIT:
       S = new (Context) CXXDefaultInitExpr(Empty);
       break;
index 0f50d7a42eab939a0b305639a1d119006e91f1c4..e36e91851703a5a8af9ba631ffa549b31fb079b5 100644 (file)
@@ -4611,6 +4611,11 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
         AddSourceLocation(Update.getLoc(), Record);
         break;
 
+      case UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT:
+        AddStmt(const_cast<Expr*>(
+                  cast<ParmVarDecl>(Update.getDecl())->getDefaultArg()));
+        break;
+
       case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: {
         auto *RD = cast<CXXRecordDecl>(D);
         UpdatedDeclContexts.insert(RD->getPrimaryContext());
@@ -5779,6 +5784,15 @@ void ASTWriter::StaticDataMemberInstantiated(const VarDecl *D) {
        D->getMemberSpecializationInfo()->getPointOfInstantiation()));
 }
 
+void ASTWriter::DefaultArgumentInstantiated(const ParmVarDecl *D) {
+  assert(!WritingAST && "Already writing the AST!");
+  if (!D->isFromASTFile())
+    return;
+
+  DeclUpdates[D].push_back(
+      DeclUpdate(UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT, D));
+}
+
 void ASTWriter::AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
                                              const ObjCInterfaceDecl *IFD) {
   assert(!WritingAST && "Already writing the AST!");
index e52ed052d3bc27b5c3f234b51bf9ac2ef06075b2..000a2185f5f0b20b77d08743eea4486a0cf76b39 100644 (file)
@@ -1336,15 +1336,8 @@ void ASTStmtWriter::VisitCXXThrowExpr(CXXThrowExpr *E) {
 
 void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
   VisitExpr(E);
-
-  bool HasOtherExprStored = E->Param.getInt();
-  // Store these first, the reader reads them before creation.
-  Record.push_back(HasOtherExprStored);
-  if (HasOtherExprStored)
-    Writer.AddStmt(E->getExpr());
   Writer.AddDeclRef(E->getParam(), Record);
   Writer.AddSourceLocation(E->getUsedLocation(), Record);
-
   Code = serialization::EXPR_CXX_DEFAULT_ARG;
 }
 
diff --git a/test/PCH/chain-default-argument-instantiation.cpp b/test/PCH/chain-default-argument-instantiation.cpp
new file mode 100644 (file)
index 0000000..0accd54
--- /dev/null
@@ -0,0 +1,50 @@
+// Test default argument instantiation in chained PCH.
+
+// Without PCH
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -include %s -include %s %s
+
+// With PCH
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -chain-include %s -chain-include %s
+
+// With modules
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -fmodules %s -chain-include %s -chain-include %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER1
+#define HEADER1
+//===----------------------------------------------------------------------===//
+// Primary header.
+
+namespace rdar23810407 {
+  template<typename T> int f(T t) {
+    extern T rdar23810407_variable;
+    return 0;
+  }
+  template<typename T> int g(int a = f([] {}));
+}
+
+//===----------------------------------------------------------------------===//
+#elif not defined(HEADER2)
+#define HEADER2
+#if !defined(HEADER1)
+#error Header inclusion order messed up
+#endif
+
+//===----------------------------------------------------------------------===//
+// Dependent header.
+
+inline void instantiate_once() {
+  rdar23810407::g<int>();
+}
+
+//===----------------------------------------------------------------------===//
+#else
+//===----------------------------------------------------------------------===//
+
+void test() {
+  rdar23810407::g<int>();
+}
+
+//===----------------------------------------------------------------------===//
+#endif
index fe449c8ccb4a2e2d8284fa54f3681b0ce4a2275e..89751a493eef69831d4133c5ff279d1e60b19bf5 100644 (file)
@@ -94,9 +94,9 @@ namespace test4 {
   // FIXME: We should warn for non-dependent args (only when the param type is also non-dependent) only once
   // not once for the template + once for every instantiation
   template<typename T>
-  void tmpl(char c = NULL, // expected-warning 4 {{implicit conversion of NULL constant to 'char'}}
+  void tmpl(char c = NULL, // expected-warning 3 {{implicit conversion of NULL constant to 'char'}}
             T a = NULL, // expected-warning {{implicit conversion of NULL constant to 'char'}} \
-                           expected-warning {{implicit conversion of NULL constant to 'int'}}
+                           expected-warning {{implicit conversion of NULL constant to 'int'}}
             T b = 1024) { // expected-warning {{implicit conversion from 'int' to 'char' changes value from 1024 to 0}}
   }
 
@@ -107,8 +107,7 @@ namespace test4 {
   void func() {
     tmpl<char>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<char>' required here}}
     tmpl<int>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<int>' required here}}
-    // FIXME: We should warn only once for each template instantiation - not once for each call
-    tmpl<int>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<int>' required here}}
+    tmpl<int>();
     tmpl2<int*>();
   }
 }
index 4cfd7a5843f01bd601db01a9788277456b3eb397..0c97c2056b7576052396629a459ec1a720dd4560 100644 (file)
@@ -56,3 +56,22 @@ namespace PR16975 {
 
   baz data{0};
 }
+
+// rdar://23810407
+// An IRGen failure due to a symbol collision due to a default argument
+// being instantiated twice.  Credit goes to Richard Smith for this
+// reduction to a -fsyntax-only failure.
+namespace rdar23810407 {
+  // Instantiating the default argument multiple times will produce two
+  // different lambda types and thus instantiate this function multiple
+  // times, which will produce conflicting extern variable declarations.
+  template<typename T> int f(T t) {
+    extern T rdar23810407_variable;
+    return 0;
+  }
+  template<typename T> int g(int a = f([] {}));
+  void test() {
+    g<int>();
+    g<int>();
+  }
+}