[clang] 00068c4 - Improve diagnostics for constant evaluation that fails because a
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 8 18:14:39 PDT 2020
Author: Richard Smith
Date: 2020-07-08T18:14:23-07:00
New Revision: 00068c452a599c328986e8afcbb3311331d09d26
URL: https://github.com/llvm/llvm-project/commit/00068c452a599c328986e8afcbb3311331d09d26
DIFF: https://github.com/llvm/llvm-project/commit/00068c452a599c328986e8afcbb3311331d09d26.diff
LOG: Improve diagnostics for constant evaluation that fails because a
variable's initializer is not known.
The hope is that a better diagnostic for this case will reduce the rate
at which duplicates of non-bug PR41093 are reported.
Added:
Modified:
clang/include/clang/Basic/DiagnosticASTKinds.td
clang/lib/AST/ExprConstant.cpp
clang/test/CXX/expr/expr.const/p2-0x.cpp
clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp
clang/test/SemaCXX/constant-expression-cxx11.cpp
clang/test/SemaCXX/constant-expression-cxx1y.cpp
clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 905e3158cf40..10bedaaf7aba 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -101,8 +101,16 @@ def note_constexpr_null_subobject : Note<
"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
diff erent 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">;
@@ -159,6 +167,9 @@ def note_constexpr_access_mutable : Note<
"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<
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 9eba40c44ddc..d6dbfb14e60b 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3025,7 +3025,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
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()];
@@ -3056,12 +3056,34 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
}
// 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;
}
@@ -3072,13 +3094,6 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
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;
@@ -3088,13 +3103,24 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
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;
}
@@ -3797,6 +3823,11 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
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) {
@@ -3814,9 +3845,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
} 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) {
@@ -3829,27 +3858,29 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
}
} 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);
diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp
index c418767f8d12..b9235eeeb172 100644
--- a/clang/test/CXX/expr/expr.const/p2-0x.cpp
+++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp
@@ -376,7 +376,7 @@ namespace References {
int &d = c;
constexpr int e = 42;
int &f = const_cast<int&>(e);
- extern int &g;
+ extern int &g; // expected-note {{here}}
constexpr int &h(); // expected-note {{here}}
int &i = h(); // expected-note {{here}}
constexpr int &j() { return b; }
@@ -390,7 +390,7 @@ namespace References {
int D2 : &d - &c + 1;
int E : e / 2;
int F : f - 11;
- int G : g; // expected-error {{constant expression}}
+ int G : g; // expected-error {{constant expression}} expected-note {{initializer of 'g' is unknown}}
int H : h(); // expected-error {{constant expression}} expected-note {{undefined function 'h'}}
int I : i; // expected-error {{constant expression}} expected-note {{initializer of 'i' is not a constant expression}}
int J : j();
diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp
index 6c0df1aff231..ecb82372bcb4 100644
--- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp
+++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -std=c++98 -verify %s
+// RUN: %clang_cc1 -std=c++98 -verify=cxx98 %s
+// RUN: %clang_cc1 -std=c++11 -verify=cxx11 %s
+// cxx11-no-diagnostics
template<int n> struct S;
@@ -13,9 +15,9 @@ template<int n> struct T {
// - a constant with literal type and is initialized with an expression
// that is value-dependent.
const int k = n;
- typename S<k>::T check3; // ok, u is value-dependent
+ typename S<k>::T check3; // ok, k is value-dependent
- const int &i = k;
- typename S<i>::T check4; // expected-error {{not an integral constant expression}}
+ const int &i = k; // cxx98-note {{declared here}}
+ typename S<i>::T check4; // cxx98-error {{not an integral constant expression}} cxx98-note {{read of variable 'i' of non-integral, non-enumeration type 'const int &'}}
}
};
diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp
index dd6d7ccba19f..78e9fef96c8d 100644
--- a/clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -682,12 +682,12 @@ template<bool B, typename T> struct S : T {
}
};
-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;
@@ -1258,7 +1258,7 @@ constexpr int m1b = const_cast<const int&>(n1); // expected-error {{constant exp
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'}}
@@ -1372,7 +1372,7 @@ namespace InstantiateCaseStmt {
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);
@@ -1381,9 +1381,9 @@ namespace ConvertedConstantExpr {
// 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}}
};
@@ -2302,3 +2302,23 @@ namespace PR41854 {
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);
+ }
+}
diff --git a/clang/test/SemaCXX/constant-expression-cxx1y.cpp b/clang/test/SemaCXX/constant-expression-cxx1y.cpp
index 5a414799f755..8bc4f88a63a9 100644
--- a/clang/test/SemaCXX/constant-expression-cxx1y.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx1y.cpp
@@ -1009,7 +1009,7 @@ constexpr int sum(const char (&Arr)[N]) {
// As an extension, we support evaluating some things that are `const` as though
// they were `constexpr` when folding, but it should not be allowed in normal
// constexpr evaluation.
-const char Cs[] = {'a', 'b'};
+const char Cs[] = {'a', 'b'}; // expected-note 2{{declared here}}
void foo() __attribute__((enable_if(sum(Cs) == 'a' + 'b', "")));
void run() { foo(); }
diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
index a16a5b54d8e0..fc49ec88d553 100644
--- a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
+++ b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
@@ -5,7 +5,7 @@
#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 {
@@ -237,7 +237,7 @@ namespace in_class_template {
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;
@@ -250,11 +250,11 @@ namespace in_class_template {
// 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.
More information about the cfe-commits
mailing list