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