r291318 - PR23135: Don't instantiate constexpr functions referenced in unevaluated operands where possible.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 6 16:48:55 PST 2017


Author: rsmith
Date: Fri Jan  6 18:48:55 2017
New Revision: 291318

URL: http://llvm.org/viewvc/llvm-project?rev=291318&view=rev
Log:
PR23135: Don't instantiate constexpr functions referenced in unevaluated operands where possible.

This implements something like the current direction of DR1581: we use a narrow
syntactic check to determine the set of places where a constant expression
could be evaluated, and only instantiate a constexpr function or variable if
it's referenced in one of those contexts, or is odr-used.

It's not yet clear whether this is the right set of syntactic locations; we
currently consider all contexts within templates that would result in odr-uses
after instantiation, and contexts within list-initialization (narrowing
conversions take another victim...), as requiring instantiation. We could in
principle restrict the former cases more (only const integral / reference
variable initializers, and contexts in which a constant expression is required,
perhaps). However, this is sufficient to allow us to accept libstdc++ code,
which relies on GCC's behavior (which appears to be somewhat similar to this
approach).

Modified:
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseInit.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprMember.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaLambda.cpp
    cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp
    cfe/trunk/test/CXX/temp/temp.param/p5.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
    cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp
    cfe/trunk/test/SemaCXX/member-init.cpp
    cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp
    cfe/trunk/test/SemaTemplate/default-arguments-cxx0x.cpp
    cfe/trunk/test/SemaTemplate/instantiate-init.cpp

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Jan  6 18:48:55 2017
@@ -807,6 +807,12 @@ public:
     /// run time.
     Unevaluated,
 
+    /// \brief The current expression occurs within a braced-init-list within
+    /// an unevaluated operand. This is mostly like a regular unevaluated
+    /// context, except that we still instantiate constexpr functions that are
+    /// referenced here so that we can perform narrowing checks correctly.
+    UnevaluatedList,
+
     /// \brief The current expression occurs within a discarded statement.
     /// This behaves largely similarly to an unevaluated operand in preventing
     /// definitions from being required, but not in other ways.
@@ -899,7 +905,8 @@ public:
     MangleNumberingContext &getMangleNumberingContext(ASTContext &Ctx);
 
     bool isUnevaluated() const {
-      return Context == Unevaluated || Context == UnevaluatedAbstract;
+      return Context == Unevaluated || Context == UnevaluatedAbstract ||
+             Context == UnevaluatedList;
     }
   };
 
@@ -10194,6 +10201,22 @@ public:
                                             IsDecltype);
   }
 
+  enum InitListTag { InitList };
+  EnterExpressionEvaluationContext(Sema &Actions, InitListTag,
+                                   bool ShouldEnter = true)
+      : Actions(Actions), Entered(false) {
+    // In C++11 onwards, narrowing checks are performed on the contents of
+    // braced-init-lists, even when they occur within unevaluated operands.
+    // Therefore we still need to instantiate constexpr functions used in such
+    // a context.
+    if (ShouldEnter && Actions.isUnevaluatedContext() &&
+        Actions.getLangOpts().CPlusPlus11) {
+      Actions.PushExpressionEvaluationContext(Sema::UnevaluatedList, nullptr,
+                                              false);
+      Entered = true;
+    }
+  }
+
   ~EnterExpressionEvaluationContext() {
     if (Entered)
       Actions.PopExpressionEvaluationContext();

Modified: cfe/trunk/lib/Parse/ParseInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseInit.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseInit.cpp (original)
+++ cfe/trunk/lib/Parse/ParseInit.cpp Fri Jan  6 18:48:55 2017
@@ -404,6 +404,10 @@ ExprResult Parser::ParseBraceInitializer
     return Actions.ActOnInitList(LBraceLoc, None, ConsumeBrace());
   }
 
+  // Enter an appropriate expression evaluation context for an initializer list.
+  EnterExpressionEvaluationContext EnterContext(
+      Actions, EnterExpressionEvaluationContext::InitList);
+
   bool InitExprsOk = true;
 
   while (1) {

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Jan  6 18:48:55 2017
@@ -9828,9 +9828,14 @@ Sema::ComputeDefaultedDefaultCtorExcepti
   }
 
   // Field constructors.
-  for (const auto *F : ClassDecl->fields()) {
+  for (auto *F : ClassDecl->fields()) {
     if (F->hasInClassInitializer()) {
-      if (Expr *E = F->getInClassInitializer())
+      Expr *E = F->getInClassInitializer();
+      if (!E)
+        // FIXME: It's a little wasteful to build and throw away a
+        // CXXDefaultInitExpr here.
+        E = BuildCXXDefaultInitExpr(Loc, F).get();
+      if (E)
         ExceptSpec.CalledExpr(E);
     } else if (const RecordType *RecordTy
               = Context.getBaseElementType(F->getType())->getAs<RecordType>()) {
@@ -12291,6 +12296,10 @@ ExprResult Sema::BuildCXXDefaultInitExpr
   if (Field->getInClassInitializer())
     return CXXDefaultInitExpr::Create(Context, Loc, Field);
 
+  // If we might have already tried and failed to instantiate, don't try again.
+  if (Field->isInvalidDecl())
+    return ExprError();
+
   // Maybe we haven't instantiated the in-class initializer. Go check the
   // pattern FieldDecl to see if it has one.
   CXXRecordDecl *ParentRD = cast<CXXRecordDecl>(Field->getParent());
@@ -12320,8 +12329,11 @@ ExprResult Sema::BuildCXXDefaultInitExpr
     }
 
     if (InstantiateInClassInitializer(Loc, Field, Pattern,
-                                      getTemplateInstantiationArgs(Field)))
+                                      getTemplateInstantiationArgs(Field))) {
+      // Don't diagnose this again.
+      Field->setInvalidDecl();
       return ExprError();
+    }
     return CXXDefaultInitExpr::Create(Context, Loc, Field);
   }
 
@@ -12344,6 +12356,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr
       << OutermostClass << Field;
   Diag(Field->getLocEnd(), diag::note_in_class_initializer_not_yet_parsed);
 
+  // Don't diagnose this again.
+  Field->setInvalidDecl();
   return ExprError();
 }
 

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri Jan  6 18:48:55 2017
@@ -13150,41 +13150,63 @@ ExprResult Sema::HandleExprEvaluationCon
   return TransformToPotentiallyEvaluated(E);
 }
 
-static bool IsPotentiallyEvaluatedContext(Sema &SemaRef) {
-  // Do not mark anything as "used" within a dependent context; wait for
-  // an instantiation.
-  if (SemaRef.CurContext->isDependentContext())
-    return false;
-
+/// Are we within a context in which some evaluation could be performed (be it
+/// constant evaluation or runtime evaluation)? Sadly, this notion is not quite
+/// captured by C++'s idea of an "unevaluated context".
+static bool isEvaluatableContext(Sema &SemaRef) {
   switch (SemaRef.ExprEvalContexts.back().Context) {
     case Sema::Unevaluated:
     case Sema::UnevaluatedAbstract:
-      // We are in an expression that is not potentially evaluated; do nothing.
-      // (Depending on how you read the standard, we actually do need to do
-      // something here for null pointer constants, but the standard's
-      // definition of a null pointer constant is completely crazy.)
+    case Sema::DiscardedStatement:
+      // Expressions in this context are never evaluated.
       return false;
 
+    case Sema::UnevaluatedList:
+    case Sema::ConstantEvaluated:
+    case Sema::PotentiallyEvaluated:
+      // Expressions in this context could be evaluated.
+      return true;
+
+    case Sema::PotentiallyEvaluatedIfUsed:
+      // Referenced declarations will only be used if the construct in the
+      // containing expression is used, at which point we'll be given another
+      // turn to mark them.
+      return false;
+  }
+  llvm_unreachable("Invalid context");
+}
+
+/// Are we within a context in which references to resolved functions or to
+/// variables result in odr-use?
+static bool isOdrUseContext(Sema &SemaRef, bool SkipDependentUses = true) {
+  // An expression in a template is not really an expression until it's been
+  // instantiated, so it doesn't trigger odr-use.
+  if (SkipDependentUses && SemaRef.CurContext->isDependentContext())
+    return false;
+
+  switch (SemaRef.ExprEvalContexts.back().Context) {
+    case Sema::Unevaluated:
+    case Sema::UnevaluatedList:
+    case Sema::UnevaluatedAbstract:
     case Sema::DiscardedStatement:
-      // These are technically a potentially evaluated but they have the effect
-      // of suppressing use marking.
       return false;
 
     case Sema::ConstantEvaluated:
     case Sema::PotentiallyEvaluated:
-      // We are in a potentially evaluated expression (or a constant-expression
-      // in C++03); we need to do implicit template instantiation, implicitly
-      // define class members, and mark most declarations as used.
       return true;
 
     case Sema::PotentiallyEvaluatedIfUsed:
-      // Referenced declarations will only be used if the construct in the
-      // containing expression is used.
       return false;
   }
   llvm_unreachable("Invalid context");
 }
 
+static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) {
+  CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Func);
+  return Func->isConstexpr() &&
+         (Func->isImplicitlyInstantiable() || (MD && !MD->isUserProvided()));
+}
+
 /// \brief Mark a function referenced, and check whether it is odr-used
 /// (C++ [basic.def.odr]p2, C99 6.9p3)
 void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
@@ -13200,7 +13222,7 @@ void Sema::MarkFunctionReferenced(Source
   //
   // We (incorrectly) mark overload resolution as an unevaluated context, so we
   // can just check that here.
-  bool OdrUse = MightBeOdrUse && IsPotentiallyEvaluatedContext(*this);
+  bool OdrUse = MightBeOdrUse && isOdrUseContext(*this);
 
   // Determine whether we require a function definition to exist, per
   // C++11 [temp.inst]p3:
@@ -13209,27 +13231,11 @@ void Sema::MarkFunctionReferenced(Source
   //   specialization is implicitly instantiated when the specialization is
   //   referenced in a context that requires a function definition to exist.
   //
-  // We consider constexpr function templates to be referenced in a context
-  // that requires a definition to exist whenever they are referenced.
-  //
-  // FIXME: This instantiates constexpr functions too frequently. If this is
-  // really an unevaluated context (and we're not just in the definition of a
-  // function template or overload resolution or other cases which we
-  // incorrectly consider to be unevaluated contexts), and we're not in a
-  // subexpression which we actually need to evaluate (for instance, a
-  // template argument, array bound or an expression in a braced-init-list),
-  // we are not permitted to instantiate this constexpr function definition.
-  //
-  // FIXME: This also implicitly defines special members too frequently. They
-  // are only supposed to be implicitly defined if they are odr-used, but they
-  // are not odr-used from constant expressions in unevaluated contexts.
-  // However, they cannot be referenced if they are deleted, and they are
-  // deleted whenever the implicit definition of the special member would
-  // fail (with very few exceptions).
-  CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Func);
+  // That is either when this is an odr-use, or when a usage of a constexpr
+  // function occurs within an evaluatable context.
   bool NeedDefinition =
-      OdrUse || (Func->isConstexpr() && (Func->isImplicitlyInstantiable() ||
-                                         (MD && !MD->isUserProvided())));
+      OdrUse || (isEvaluatableContext(*this) &&
+                 isImplicitlyDefinableConstexprFunction(Func));
 
   // C++14 [temp.expl.spec]p6:
   //   If a template [...] is explicitly specialized then that specialization
@@ -14122,13 +14128,12 @@ static void DoMarkVarDeclReferenced(Sema
          "Invalid Expr argument to DoMarkVarDeclReferenced");
   Var->setReferenced();
 
-  if (SemaRef.isUnevaluatedContext())
-    return;
-
   TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind();
-  bool MarkODRUsed = IsPotentiallyEvaluatedContext(SemaRef);
+
+  bool OdrUseContext = isOdrUseContext(SemaRef);
   bool NeedDefinition =
-      MarkODRUsed || Var->isUsableInConstantExpressions(SemaRef.Context);
+      OdrUseContext || (isEvaluatableContext(SemaRef) &&
+                        Var->isUsableInConstantExpressions(SemaRef.Context));
 
   VarTemplateSpecializationDecl *VarSpec =
       dyn_cast<VarTemplateSpecializationDecl>(Var);
@@ -14193,18 +14198,20 @@ static void DoMarkVarDeclReferenced(Sema
   // Note that we use the C++11 definition everywhere because nothing in
   // C++03 depends on whether we get the C++03 version correct. The second
   // part does not apply to references, since they are not objects.
-  if (MarkODRUsed && E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
+  if (OdrUseContext && E &&
+      IsVariableAConstantExpression(Var, SemaRef.Context)) {
     // A reference initialized by a constant expression can never be
     // odr-used, so simply ignore it.
     if (!Var->getType()->isReferenceType())
       SemaRef.MaybeODRUseExprs.insert(E);
-  } else if (MarkODRUsed) {
+  } else if (OdrUseContext) {
     MarkVarDeclODRUsed(Var, Loc, SemaRef,
                        /*MaxFunctionScopeIndex ptr*/ nullptr);
-  } else {
-    // If we don't yet know whether this context is going to end up being an
-    // evaluated context, and we're referencing a variable from an enclosing
-    // scope, add a potential capture.
+  } else if (isOdrUseContext(SemaRef, /*SkipDependentUses*/false)) {
+    // If this is a dependent context, we don't need to mark variables as
+    // odr-used, but we may still need to track them for lambda capture.
+    // FIXME: Do we also need to do this inside dependent typeid expressions
+    // (which are modeled as unevaluated at this point)?
     const bool RefersToEnclosingScope =
         (SemaRef.CurContext != Var->getDeclContext() &&
          Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage());
@@ -14321,9 +14328,13 @@ void Sema::MarkAnyDeclReferenced(SourceL
 }
 
 namespace {
-  // Mark all of the declarations referenced
+  // Mark all of the declarations used by a type as referenced.
   // FIXME: Not fully implemented yet! We need to have a better understanding
-  // of when we're entering
+  // of when we're entering a context we should not recurse into.
+  // FIXME: This is and EvaluatedExprMarker are more-or-less equivalent to
+  // TreeTransforms rebuilding the type in a new context. Rather than
+  // duplicating the TreeTransform logic, we should consider reusing it here.
+  // Currently that causes problems when rebuilding LambdaExprs.
   class MarkReferencedDecls : public RecursiveASTVisitor<MarkReferencedDecls> {
     Sema &S;
     SourceLocation Loc;
@@ -14462,6 +14473,7 @@ bool Sema::DiagRuntimeBehavior(SourceLoc
                                const PartialDiagnostic &PD) {
   switch (ExprEvalContexts.back().Context) {
   case Unevaluated:
+  case UnevaluatedList:
   case UnevaluatedAbstract:
   case DiscardedStatement:
     // The argument will never be evaluated, so don't complain.

Modified: cfe/trunk/lib/Sema/SemaExprMember.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprMember.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprMember.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprMember.cpp Fri Jan  6 18:48:55 2017
@@ -134,6 +134,7 @@ static IMAKind ClassifyImplicitMemberAcc
   assert(!AbstractInstanceResult);
   switch (SemaRef.ExprEvalContexts.back().Context) {
   case Sema::Unevaluated:
+  case Sema::UnevaluatedList:
     if (isField && SemaRef.getLangOpts().CPlusPlus11)
       AbstractInstanceResult = IMA_Field_Uneval_Context;
     break;

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Fri Jan  6 18:48:55 2017
@@ -6561,6 +6561,13 @@ InitializationSequence::Perform(Sema &S,
     break;
   }
 
+  // Promote from an unevaluated context to an unevaluated list context in
+  // C++11 list-initialization; we need to instantiate entities usable in
+  // constant expressions here in order to perform narrowing checks =(
+  EnterExpressionEvaluationContext Evaluated(
+      S, EnterExpressionEvaluationContext::InitList,
+      CurInit.get() && isa<InitListExpr>(CurInit.get()));
+
   // C++ [class.abstract]p2:
   //   no objects of an abstract class can be created except as subobjects
   //   of a class derived from it

Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Fri Jan  6 18:48:55 2017
@@ -1565,6 +1565,7 @@ ExprResult Sema::BuildLambdaExpr(SourceL
     //   A lambda-expression shall not appear in an unevaluated operand
     //   (Clause 5).
     case Unevaluated:
+    case UnevaluatedList:
     case UnevaluatedAbstract:
     // C++1y [expr.const]p2:
     //   A conditional-expression e is a core constant expression unless the

Modified: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp (original)
+++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp Fri Jan  6 18:48:55 2017
@@ -139,11 +139,11 @@ namespace NonLocalLambdaInstantation {
   }
 
   template<typename T>
-  struct X2 { // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2<int *>::x' requested here}}
+  struct X2 {
     int x = []{ return T(); }(); // expected-error{{cannot initialize a member subobject of type 'int' with an rvalue of type 'int *'}}
   };
 
   X2<int> x2i;
   X2<float> x2f;
-  X2<int*> x2ip; // expected-note{{implicit default constructor for 'NonLocalLambdaInstantation::X2<int *>' first required here}}
+  X2<int*> x2ip; // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2<int *>::x'}}
 }

Modified: cfe/trunk/test/CXX/temp/temp.param/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.param/p5.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.param/p5.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.param/p5.cpp Fri Jan  6 18:48:55 2017
@@ -1,13 +1,13 @@
-// RUN: %clang_cc1 -verify %s -std=c++11
+// RUN: %clang_cc1 -verify %s -std=c++14
 
-template<const int I> struct S { // expected-note {{instantiation}}
+template<const int I> struct S {
   decltype(I) n;
   int &&r = I; // expected-warning 2{{binding reference member 'r' to a temporary value}} expected-note 2{{declared here}}
 };
-S<5> s;
+S<5> s; // expected-note {{instantiation}}
 
-template<typename T, T v> struct U { // expected-note {{instantiation}}
+template<typename T, T v> struct U {
   decltype(v) n;
   int &&r = v; // expected-warning {{binding reference member 'r' to a temporary value}} expected-note {{declared here}}
 };
-U<const int, 6> u;
+U<const int, 6> u; // expected-note {{instantiation}}

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Fri Jan  6 18:48:55 2017
@@ -1902,9 +1902,9 @@ namespace ZeroSizeTypes {
 namespace BadDefaultInit {
   template<int N> struct X { static const int n = N; };
 
-  struct A { // expected-error {{default member initializer for 'k' needed within definition of enclosing class}}
+  struct A {
     int k = // expected-note {{default member initializer declared here}}
-        X<A().k>::n; // expected-error {{not a constant expression}} expected-note {{implicit default constructor for 'BadDefaultInit::A' first required here}}
+        X<A().k>::n; // expected-error {{default member initializer for 'k' needed within definition of enclosing class}}
   };
 
   // FIXME: The "constexpr constructor must initialize all members" diagnostic

Modified: cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp (original)
+++ cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp Fri Jan  6 18:48:55 2017
@@ -16,34 +16,32 @@ namespace InClassInitializers {
   // Noexcept::Noexcept is not declared constexpr, therefore noexcept(Noexcept())
   // is false.
   bool ThrowSomething() noexcept(false);
-  struct ConstExpr { // expected-error {{default member initializer for 'b' needed}}
-    bool b = noexcept(ConstExpr()) && ThrowSomething(); // expected-note {{declared here}}
-  // expected-note at -1 {{implicit default constructor for 'InClassInitializers::ConstExpr' first required here}}
+  struct ConstExpr {
+    bool b = // expected-note {{declared here}}
+      noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{default member initializer for 'b' needed}}
   };
 
   // Much more obviously broken: we can't parse the initializer without already
   // knowing whether it produces a noexcept expression.
-  struct TemplateArg { // expected-error {{default member initializer for 'n' needed}}
-    int n = ExceptionIf<noexcept(TemplateArg())>::f(); // expected-note {{declared here}}
-    // expected-note at -1 {{implicit default constructor for 'InClassInitializers::TemplateArg' first required here}}
+  struct TemplateArg {
+    int n = // expected-note {{declared here}}
+      ExceptionIf<noexcept(TemplateArg())>::f(); // expected-error {{default member initializer for 'n' needed}}
   };
 
   // And within a nested class.
-  struct Nested { // expected-note {{implicit default constructor for 'InClassInitializers::Nested::Inner' first required here}}
-    struct Inner { // expected-error {{default member initializer for 'n' needed}}
+  struct Nested {
+    struct Inner {
       int n = // expected-note {{declared here}}
-        ExceptionIf<noexcept(Nested())>::f(); // expected-note {{implicit default constructor for 'InClassInitializers::Nested' first required here}}
-    } inner;
+        ExceptionIf<noexcept(Nested())>::f();
+    } inner; // expected-error {{default member initializer for 'n' needed}}
   };
 
-  struct Nested2 { // expected-error {{implicit default constructor for 'InClassInitializers::Nested2' must explicitly initialize the member 'inner' which does not have a default constructor}}
+  struct Nested2 {
     struct Inner;
-    int n = Inner().n; // expected-note {{implicit default constructor for 'InClassInitializers::Nested2::Inner' first required here}}
-    struct Inner { // expected-error {{initializer for 'n' needed}} expected-note {{declared here}}
-      // expected-note at +1 {{declared here}}
-      int n = ExceptionIf<noexcept(Nested2())>::f();
-      // expected-note at -1 {{implicit default constructor for 'InClassInitializers::Nested2' first required here}}
-    } inner; // expected-note {{member is declared here}}
+    int n = Inner().n; // expected-error {{initializer for 'n' needed}}
+    struct Inner {
+      int n = ExceptionIf<noexcept(Nested2())>::f(); // expected-note {{declared here}}
+    } inner;
   };
 }
 

Modified: cfe/trunk/test/SemaCXX/member-init.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/member-init.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/member-init.cpp (original)
+++ cfe/trunk/test/SemaCXX/member-init.cpp Fri Jan  6 18:48:55 2017
@@ -13,10 +13,10 @@ public:
 
 bool b();
 int k;
-struct Recurse { // expected-error {{initializer for 'n' needed}}
+struct Recurse {
   int &n = // expected-note {{declared here}}
       b() ?
-      Recurse().n : // expected-note {{implicit default constructor for 'Recurse' first required here}}
+      Recurse().n : // expected-error {{initializer for 'n' needed}}
       k;
 };
 
@@ -128,21 +128,19 @@ A::A() {}
 namespace template_default_ctor {
 struct A {
   template <typename T>
-  struct B { // expected-error {{initializer for 'm1' needed}}
+  struct B {
     int m1 = 0; // expected-note {{declared here}}
   };
-  // expected-note at +1 {{implicit default constructor for 'template_default_ctor::A::B<int>' first required here}}
-  enum { NOE = noexcept(B<int>()) };
+  enum { NOE = noexcept(B<int>()) }; // expected-error {{initializer for 'm1' needed}}
 };
 }
 
 namespace default_ctor {
 struct A {
-  struct B { // expected-error {{initializer for 'm1' needed}}
+  struct B {
     int m1 = 0; // expected-note {{declared here}}
   };
-  // expected-note at +1 {{implicit default constructor for 'default_ctor::A::B' first required here}}
-  enum { NOE = noexcept(B()) };
+  enum { NOE = noexcept(B()) }; // expected-error {{initializer for 'm1' needed}}
 };
 }
 
@@ -150,19 +148,17 @@ namespace member_template {
 struct A {
   template <typename T>
   struct B {
-    struct C { // expected-error {{initializer for 'm1' needed}}
+    struct C {
       int m1 = 0; // expected-note {{declared here}}
     };
     template <typename U>
-    struct D { // expected-error {{initializer for 'm1' needed}}
+    struct D {
       int m1 = 0; // expected-note {{declared here}}
     };
   };
   enum {
-    // expected-note at +1 {{implicit default constructor for 'member_template::A::B<int>::C' first required here}}
-    NOE1 = noexcept(B<int>::C()),
-    // expected-note at +1 {{implicit default constructor for 'member_template::A::B<int>::D<int>' first required here}}
-    NOE2 = noexcept(B<int>::D<int>())
+    NOE1 = noexcept(B<int>::C()), // expected-error {{initializer for 'm1' needed}}
+    NOE2 = noexcept(B<int>::D<int>()) // expected-error {{initializer for 'm1' needed}}
   };
 };
 }

Modified: cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp (original)
+++ cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp Fri Jan  6 18:48:55 2017
@@ -77,20 +77,19 @@ namespace Reference {
 }
 
 namespace Unevaluated {
-  // We follow g++ in treating any reference to a constexpr function template
-  // specialization as requiring an instantiation, even if it occurs in an
-  // unevaluated context.
+  // We follow the current proposed resolution of core issue 1581: a constexpr
+  // function template specialization requires a definition if:
+  //  * it is odr-used, or would be odr-used except that it appears within the
+  //    definition of a template, or
+  //  * it is used within a braced-init-list, where it may be necessary for
+  //    detecting narrowing conversions.
   //
-  // We go slightly further than g++, and also trigger the implicit definition
-  // of a defaulted special member in the same circumstances. This seems scary,
-  // since a lot of classes have constexpr special members in C++11, but the
-  // only observable impact should be the implicit instantiation of constexpr
-  // special member templates (defaulted special members should only be
-  // generated if they are well-formed, and non-constexpr special members in a
-  // base or member cause the class's special member to not be constexpr).
+  // We apply this both for instantiating constexpr function template
+  // specializations and for implicitly defining defaulted constexpr special
+  // member functions.
   //
-  // FIXME: None of this is required by the C++ standard. The rules in this
-  //        area are poorly specified, so this is subject to change.
+  // FIXME: None of this is required by the C++ standard yet. The rules in this
+  //        area are subject to change.
   namespace NotConstexpr {
     template<typename T> struct S {
       S() : n(0) {}
@@ -98,16 +97,35 @@ namespace Unevaluated {
       int n;
     };
     struct U : S<int> {};
-    decltype(U(U())) u; // ok, don't instantiate S<int>::S() because it wasn't declared constexpr
+    decltype(U(U())) u;
   }
   namespace Constexpr {
     template<typename T> struct S {
       constexpr S() : n(0) {}
-      constexpr S(const S&) : n(T::error) {} // expected-error {{has no members}}
+      constexpr S(const S&) : n(T::error) {}
       int n;
     };
-    struct U : S<int> {}; // expected-note {{instantiation}}
-    decltype(U(U())) u; // expected-note {{here}}
+    struct U : S<int> {};
+    decltype(U(U())) u;
+  }
+  namespace ConstexprList {
+    template<int N> struct S {
+      constexpr S() : n(0) {
+        static_assert(N >= 0, "");
+      }
+      constexpr operator int() const { return 0; }
+      int n;
+    };
+    struct U : S<0> {};
+    // ok, trigger instantiation within a list
+    decltype(char{U()}) t0;
+    decltype(new char{S<1>()}) t1; // expected-warning {{side effects}}
+    decltype((char){S<2>()}) t2;
+    decltype(+(char[1]){{S<3>()}}) t3;
+    // do not trigger instantiation outside a list
+    decltype(char(S<-1>())) u1;
+    decltype(new char(S<-2>())) u2; // expected-warning {{side effects}}
+    decltype((char)(S<-3>())) u3;
   }
 
   namespace PR11851_Comment0 {
@@ -190,6 +208,32 @@ namespace Unevaluated {
       constexpr duration max = duration();
     }
   }
+
+  // For variables, we instantiate when they are used in a context in which
+  // evaluation could be required (odr-used, used in a template whose
+  // instantiations would odr-use, or used in list initialization), if they
+  // can be used as a constant (const integral or constexpr).
+  namespace Variables {
+    template<int N> struct A {
+      static const int k;
+      static int n;
+    };
+    template<const int *N> struct B {};
+    template<int N> constexpr int A<N>::k = *(int[N]){N}; // expected-error 1+{{negative}}
+    template<int N> int A<N>::n = *(int[N]){0};
+
+    template <typename> void f() {
+      (void)A<-1>::n; // ok
+      (void)A<-1>::k; // expected-note {{instantiation of }}
+      B<&A<-2>::n> b1; // ok
+      B<&A<-2>::k> b2; // expected-note {{instantiation of }}
+    };
+
+    decltype(A<-3>::k) d1 = 0; // ok
+    decltype(char{A<-4>::k}) d2 = 0; // expected-note {{instantiation of }} expected-error {{narrow}} expected-note {{cast}}
+    decltype(char{A<1>::k}) d3 = 0; // ok
+    decltype(char{A<1 + (unsigned char)-1>::k}) d4 = 0; // expected-error {{narrow}} expected-note {{cast}}
+  }
 }
 
 namespace NoInstantiationWhenSelectingOverload {
@@ -201,10 +245,10 @@ namespace NoInstantiationWhenSelectingOv
     int n;
   };
 
-  int f(S);
-  int f(int);
+  constexpr int f(S) { return 0; }
+  constexpr int f(int) { return 0; }
 
   void g() { f(0); }
-  void h() { (void)sizeof(f(0)); }
-  void i() { (void)sizeof(f("oops")); } // expected-note {{instantiation of}}
+  void h() { (void)sizeof(char{f(0)}); }
+  void i() { (void)sizeof(char{f("oops")}); } // expected-note {{instantiation of}}
 }

Modified: cfe/trunk/test/SemaTemplate/default-arguments-cxx0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/default-arguments-cxx0x.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/default-arguments-cxx0x.cpp (original)
+++ cfe/trunk/test/SemaTemplate/default-arguments-cxx0x.cpp Fri Jan  6 18:48:55 2017
@@ -50,6 +50,8 @@ namespace PR16975 {
     bar(T);
   };
 
+  bar<> foo{0};
+
   struct baz : public bar<> {
     using bar::bar;
   };

Modified: cfe/trunk/test/SemaTemplate/instantiate-init.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/instantiate-init.cpp?rev=291318&r1=291317&r2=291318&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/instantiate-init.cpp (original)
+++ cfe/trunk/test/SemaTemplate/instantiate-init.cpp Fri Jan  6 18:48:55 2017
@@ -115,9 +115,8 @@ namespace PR13064 {
   struct A { explicit A(int); }; // expected-note{{here}}
   template<typename T> struct B { T a { 0 }; };
   B<A> b;
-  // expected-note at +1 {{in instantiation of default member initializer}}
   template<typename T> struct C { T a = { 0 }; }; // expected-error{{explicit}}
-  C<A> c; // expected-note{{here}}
+  C<A> c; // expected-note {{in instantiation of default member initializer}}
 }
 
 namespace PR16903 {




More information about the cfe-commits mailing list