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

Mehdi Amini via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 22 23:31:32 PST 2017


Hi Richard,

I have a link failure in WebKit following this patch, the move-assign operator for an nested class isn’t emitted (IRGen) in some context where it is needed.

I’m working on running creduce the test-case which is pretty large, but in case it rings a bell, I let you know already.

— 
Mehdi

> On Jan 6, 2017, at 4:48 PM, Richard Smith via cfe-commits <cfe-commits at lists.llvm.org> wrote:
> 
> 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 {
> 
> 
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits



More information about the cfe-commits mailing list