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@-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@-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@+1 {{declared here}} - int n = ExceptionIf<noexcept(Nested2())>::f(); - // expected-note@-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@+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@+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@+1 {{implicit default constructor for 'member_template::A::B<int>::C' first required here}} - NOE1 = noexcept(B<int>::C()), - // expected-note@+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@+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@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits