"access array element of|perform pointer arithmetic on|"
"access real component of|"
"access imaginary component of}0 null pointer">;
+def note_constexpr_function_param_value_unknown : Note<
+ "function parameter %0 with unknown value cannot be used in a constant "
+ "expression">;
+def note_constexpr_var_init_unknown : Note<
+ "initializer of %0 is unknown">;
def note_constexpr_var_init_non_constant : Note<
"initializer of %0 is not a constant expression">;
+def note_constexpr_var_init_weak : Note<
+ "initializer of weak variable %0 is not considered constant because "
+ "it may be different at runtime">;
def note_constexpr_typeid_polymorphic : Note<
"typeid applied to expression of polymorphic type %0 is "
"not allowed in a constant expression in C++ standards before C++20">;
"mutable member %1 is not allowed in a constant expression">;
def note_constexpr_ltor_non_const_int : Note<
"read of non-const variable %0 is not allowed in a constant expression">;
+def note_constexpr_ltor_non_integral : Note<
+ "read of variable %0 of non-integral, non-enumeration type %1 "
+ "is not allowed in a constant expression">;
def note_constexpr_ltor_non_constexpr : Note<
"read of non-constexpr variable %0 is not allowed in a constant expression">;
def note_constexpr_ltor_incomplete_type : Note<
if (Info.checkingPotentialConstantExpression())
return false;
if (!Frame || !Frame->Arguments) {
- Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
+ Info.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << VD;
return false;
}
Result = &Frame->Arguments[PVD->getFunctionScopeIndex()];
}
// Dig out the initializer, and use the declaration which it's attached to.
+ // FIXME: We should eventually check whether the variable has a reachable
+ // initializing declaration.
const Expr *Init = VD->getAnyInitializer(VD);
- if (!Init || Init->isValueDependent()) {
- // If we're checking a potential constant expression, the variable could be
- // initialized later.
- if (!Info.checkingPotentialConstantExpression())
- Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
+ if (!Init) {
+ // Don't diagnose during potential constant expression checking; an
+ // initializer might be added later.
+ if (!Info.checkingPotentialConstantExpression()) {
+ Info.FFDiag(E, diag::note_constexpr_var_init_unknown, 1)
+ << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ }
+ return false;
+ }
+
+ if (Init->isValueDependent()) {
+ // The DeclRefExpr is not value-dependent, but the variable it refers to
+ // has a value-dependent initializer. This should only happen in
+ // constant-folding cases, where the variable is not actually of a suitable
+ // type for use in a constant expression (otherwise the DeclRefExpr would
+ // have been value-dependent too), so diagnose that.
+ assert(!VD->mightBeUsableInConstantExpressions(Info.Ctx));
+ if (!Info.checkingPotentialConstantExpression()) {
+ Info.FFDiag(E, Info.getLangOpts().CPlusPlus11
+ ? diag::note_constexpr_ltor_non_constexpr
+ : diag::note_constexpr_ltor_non_integral, 1)
+ << VD << VD->getType();
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ }
return false;
}
return true;
}
- // Never evaluate the initializer of a weak variable. We can't be sure that
- // this is the definition which will be used.
- if (VD->isWeak()) {
- Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
- return false;
- }
-
// Check that we can fold the initializer. In C++, we will have already done
// this in the cases where it matters for conformance.
SmallVector<PartialDiagnosticAt, 8> Notes;
Info.Note(VD->getLocation(), diag::note_declared_at);
Info.addNotes(Notes);
return false;
- } else if (!VD->checkInitIsICE()) {
+ }
+
+ // Check that the variable is actually usable in constant expressions.
+ if (!VD->checkInitIsICE()) {
Info.CCEDiag(E, diag::note_constexpr_var_init_non_constant,
Notes.size() + 1) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at);
Info.addNotes(Notes);
}
+ // Never use the initializer of a weak variable, not even for constant
+ // folding. We can't be sure that this is the definition that will be used.
+ if (VD->isWeak()) {
+ Info.FFDiag(E, diag::note_constexpr_var_init_weak) << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ return false;
+ }
+
Result = VD->getEvaluatedValue();
return true;
}
return CompleteObject();
}
+ // In OpenCL if a variable is in constant address space it is a const value.
+ bool IsConstant = BaseType.isConstQualified() ||
+ (Info.getLangOpts().OpenCL &&
+ BaseType.getAddressSpace() == LangAS::opencl_constant);
+
// Unless we're looking at a local variable or argument in a constexpr call,
// the variable we're reading must be const.
if (!Frame) {
} else if (BaseType->isIntegralOrEnumerationType()) {
// In OpenCL if a variable is in constant address space it is a const
// value.
- if (!(BaseType.isConstQualified() ||
- (Info.getLangOpts().OpenCL &&
- BaseType.getAddressSpace() == LangAS::opencl_constant))) {
+ if (!IsConstant) {
if (!IsAccess)
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
if (Info.getLangOpts().CPlusPlus) {
}
} else if (!IsAccess) {
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
- } else if (BaseType->isFloatingType() && BaseType.isConstQualified()) {
- // We support folding of const floating-point types, in order to make
- // static const data members of such types (supported as an extension)
- // more useful.
- if (Info.getLangOpts().CPlusPlus11) {
- Info.CCEDiag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
+ } else if (IsConstant && Info.checkingPotentialConstantExpression() &&
+ BaseType->isLiteralType(Info.Ctx) && !VD->hasDefinition()) {
+ // This variable might end up being constexpr. Don't diagnose it yet.
+ } else if (IsConstant) {
+ // Keep evaluating to see what we can do. In particular, we support
+ // folding of const floating-point types, in order to make static const
+ // data members of such types (supported as an extension) more useful.
+ if (Info.getLangOpts().CPlusPlus) {
+ Info.CCEDiag(E, Info.getLangOpts().CPlusPlus11
+ ? diag::note_constexpr_ltor_non_constexpr
+ : diag::note_constexpr_ltor_non_integral, 1)
+ << VD << BaseType;
Info.Note(VD->getLocation(), diag::note_declared_at);
} else {
Info.CCEDiag(E);
}
- } else if (BaseType.isConstQualified() && VD->hasDefinition(Info.Ctx)) {
- Info.CCEDiag(E, diag::note_constexpr_ltor_non_constexpr) << VD;
- // Keep evaluating to see what we can do.
} else {
- // FIXME: Allow folding of values of any literal type in all languages.
- if (Info.checkingPotentialConstantExpression() &&
- VD->getType().isConstQualified() && !VD->hasDefinition(Info.Ctx)) {
- // The definition of this variable could be constexpr. We can't
- // access it right now, but may be able to in future.
- } else if (Info.getLangOpts().CPlusPlus11) {
- Info.FFDiag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
+ // Never allow reading a non-const value.
+ if (Info.getLangOpts().CPlusPlus) {
+ Info.FFDiag(E, Info.getLangOpts().CPlusPlus11
+ ? diag::note_constexpr_ltor_non_constexpr
+ : diag::note_constexpr_ltor_non_integral, 1)
+ << VD << BaseType;
Info.Note(VD->getLocation(), diag::note_declared_at);
} else {
Info.FFDiag(E);
}
};
-extern const int n;
+extern const int n; // expected-note {{declared here}}
template<typename T> void f() {
// This is ill-formed, because a hypothetical instantiation at the point of
// template definition would be ill-formed due to a construct that does not
// depend on a template parameter.
- constexpr int k = n; // expected-error {{must be initialized by a constant expression}}
+ constexpr int k = n; // expected-error {{must be initialized by a constant expression}} expected-note {{initializer of 'n' is unknown}}
}
// It doesn't matter that the instantiation could later become valid:
constexpr int n = 4;
constexpr int m2b = const_cast<const int&>(n2); // expected-error {{constant expression}} expected-note {{read of volatile object 'n2'}}
struct T { int n; };
-const T t = { 42 };
+const T t = { 42 }; // expected-note {{declared here}}
constexpr int f(volatile int &&r) {
return r; // expected-note {{read of volatile-qualified type 'volatile int'}}
namespace ConvertedConstantExpr {
extern int &m;
- extern int &n;
+ extern int &n; // expected-note 2{{declared here}}
constexpr int k = 4;
int &m = const_cast<int&>(k);
// useless note and instead just point to the non-constant subexpression.
enum class E {
em = m,
- en = n, // expected-error {{not a constant expression}}
- eo = (m +
- n // expected-error {{not a constant expression}}
+ en = n, // expected-error {{not a constant expression}} expected-note {{initializer of 'n' is unknown}}
+ eo = (m + // expected-error {{not a constant expression}}
+ n // expected-note {{initializer of 'n' is unknown}}
),
eq = reinterpret_cast<long>((int*)0) // expected-error {{not a constant expression}} expected-note {{reinterpret_cast}}
};
f &d = reinterpret_cast<f&>(a);
unsigned b = d.c;
}
+
+namespace array_size {
+ template<int N> struct array {
+ static constexpr int size() { return N; }
+ };
+ template<typename T> void f1(T t) {
+ constexpr int k = t.size();
+ }
+ template<typename T> void f2(const T &t) {
+ constexpr int k = t.size(); // expected-error {{constant}} expected-note {{function parameter 't' with unknown value cannot be used in a constant expression}}
+ }
+ template<typename T> void f3(const T &t) {
+ constexpr int k = T::size();
+ }
+ void g(array<3> a) {
+ f1(a);
+ f2(a); // expected-note {{instantiation of}}
+ f3(a);
+ }
+}
#define CONST const
#ifdef PRECXX11
-#define static_assert(expr, msg) typedef int static_assert[(expr) ? 1 : -1];
+#define static_assert _Static_assert
#endif
class A {
namespace definition_after_outer_instantiation {
template<typename A> struct S {
template<typename B> static const int V1;
- template<typename B> static const int V2;
+ template<typename B> static const int V2; // expected-note 3{{here}}
};
template struct S<int>;
template<typename A> template<typename B> const int S<A>::V1 = 123;
// is instantiated. This is kind of implied by [temp.class.spec.mfunc]/2,
// and matches our behavior for member class templates, but it's not clear
// that this is intentional. See PR17294 and core-24030.
- static_assert(S<int>::V2<int*> == 456, ""); // FIXME expected-error {{}}
- static_assert(S<int>::V2<int&> == 789, ""); // expected-error {{}}
+ static_assert(S<int>::V2<int*> == 456, ""); // FIXME expected-error {{}} expected-note {{initializer of 'V2<int *>' is unknown}}
+ static_assert(S<int>::V2<int&> == 789, ""); // expected-error {{}} expected-note {{initializer of 'V2<int &>' is unknown}}
template<typename A> template<typename B> const int S<A>::V2<B&> = 789;
- static_assert(S<int>::V2<int&> == 789, ""); // FIXME expected-error {{}}
+ static_assert(S<int>::V2<int&> == 789, ""); // FIXME expected-error {{}} expected-note {{initializer of 'V2<int &>' is unknown}}
// All is OK if the partial specialization is declared before the implicit
// instantiation of the class template specialization.