[clang] Improve diagnostics for uninitialized constexpr variables

Instead of complaining about default initialization, tell users that
constexpr variables need to be initialized by a constant expression.

Differential Revision: https://reviews.llvm.org/D131662
This commit is contained in:
Timm Bäder 2022-08-11 11:22:00 +02:00
parent e001a4e489
commit 3d2ab237f1
6 changed files with 38 additions and 14 deletions

View File

@ -101,6 +101,8 @@ Improvements to Clang's diagnostics
- Clang will now print more information about failed static assertions. In
particular, simple static assertion expressions are evaluated to their
compile-time value and printed out if the assertion fails.
- Diagnostics about uninitialized ``constexpr`` varaibles have been improved
to mention the missing constant initializer.
Non-comprehensive list of changes in this release
-------------------------------------------------

View File

@ -13372,8 +13372,12 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
// Provide a specific diagnostic for uninitialized variable
// definitions with incomplete array type.
if (Type->isIncompleteArrayType()) {
Diag(Var->getLocation(),
diag::err_typecheck_incomplete_array_needs_initializer);
if (Var->isConstexpr())
Diag(Var->getLocation(), diag::err_constexpr_var_requires_const_init)
<< Var;
else
Diag(Var->getLocation(),
diag::err_typecheck_incomplete_array_needs_initializer);
Var->setInvalidDecl();
return;
}

View File

@ -8053,19 +8053,29 @@ ExprResult InitializationSequence::Perform(Sema &S,
return ExprError();
}
if (!ZeroInitializationFixit.empty()) {
unsigned DiagID = diag::err_default_init_const;
if (Decl *D = Entity.getDecl())
if (S.getLangOpts().MSVCCompat && D->hasAttr<SelectAnyAttr>())
DiagID = diag::ext_default_init_const;
const Decl *D = Entity.getDecl();
const auto *VD = dyn_cast_or_null<VarDecl>(D);
QualType DestType = Entity.getType();
// The initialization would have succeeded with this fixit. Since the fixit
// is on the error, we need to build a valid AST in this case, so this isn't
// handled in the Failed() branch above.
QualType DestType = Entity.getType();
S.Diag(Kind.getLocation(), DiagID)
<< DestType << (bool)DestType->getAs<RecordType>()
<< FixItHint::CreateInsertion(ZeroInitializationFixitLoc,
ZeroInitializationFixit);
if (!DestType->isRecordType() && VD && VD->isConstexpr()) {
// Use a more useful diagnostic for constexpr variables.
S.Diag(Kind.getLocation(), diag::err_constexpr_var_requires_const_init)
<< VD
<< FixItHint::CreateInsertion(ZeroInitializationFixitLoc,
ZeroInitializationFixit);
} else {
unsigned DiagID = diag::err_default_init_const;
if (S.getLangOpts().MSVCCompat && D && D->hasAttr<SelectAnyAttr>())
DiagID = diag::ext_default_init_const;
S.Diag(Kind.getLocation(), DiagID)
<< DestType << (bool)DestType->getAs<RecordType>()
<< FixItHint::CreateInsertion(ZeroInitializationFixitLoc,
ZeroInitializationFixit);
}
}
if (getKind() == DependentSequence) {
@ -9464,6 +9474,10 @@ bool InitializationSequence::Diagnose(Sema &S,
<< Entity.getName();
S.Diag(Entity.getDecl()->getLocation(), diag::note_previous_decl)
<< Entity.getName();
} else if (const auto *VD = dyn_cast_if_present<VarDecl>(Entity.getDecl());
VD && VD->isConstexpr()) {
S.Diag(Kind.getLocation(), diag::err_constexpr_var_requires_const_init)
<< VD;
} else {
S.Diag(Kind.getLocation(), diag::err_default_init_const)
<< DestType << (bool)DestType->getAs<RecordType>();

View File

@ -37,7 +37,7 @@ struct s2 {
#if __cplusplus <= 201402L && !defined(MS_ABI)
// expected-error@-2 {{requires an initializer}}
#else
// expected-error@-4 {{default initialization of an object of const}}
// expected-error@-4 {{constexpr variable 'mi2' must be initialized by a constant expression}}
#endif
mutable constexpr int mi3 = 3; // expected-error-re {{non-static data member cannot be constexpr{{$}}}} expected-error {{'mutable' and 'const' cannot be mixed}}
};

View File

@ -18,7 +18,7 @@ extern int (*const d)(int);
// A variable declaration which uses the constexpr specifier shall have an
// initializer and shall be initialized by a constant expression.
constexpr int ni1; // expected-error {{default initialization of an object of const type 'const int'}}
constexpr int ni1; // expected-error {{constexpr variable 'ni1' must be initialized by a constant expression}}
constexpr struct C { C(); } ni2; // expected-error {{cannot have non-literal type 'const struct C'}} expected-note 3{{has no constexpr constructors}}
constexpr double &ni3; // expected-error {{declaration of reference variable 'ni3' requires an initializer}}

View File

@ -27,6 +27,10 @@ struct MemberZero {
constexpr int zero() const { return 0; }
};
constexpr int arr[]; // expected-error {{constexpr variable 'arr' must be initialized by a constant expression}}
constexpr int arr2[2]; // expected-error {{constexpr variable 'arr2' must be initialized by a constant expression}}
constexpr int arr3[2] = {};
namespace DerivedToVBaseCast {
struct U { int n; };
@ -1298,7 +1302,7 @@ namespace ExternConstexpr {
void f() {
extern constexpr int i; // expected-error {{constexpr variable declaration must be a definition}}
constexpr int j = 0;
constexpr int k; // expected-error {{default initialization of an object of const type}}
constexpr int k; // expected-error {{constexpr variable 'k' must be initialized by a constant expression}}
}
extern const int q;