зеркало из https://github.com/microsoft/clang.git
Migrate the mish-mash of declaration checks in
Sema::ActOnUninitializedDecl over to InitializationSequence (with default initialization), eliminating redundancy. More importantly, we now check that a const definition in C++ has an initilizer, which was an #if 0'd code for many, many months. A few other tweaks were needed to get everything working again: - Fix all of the places in the testsuite where we defined const objects without initializers (now that we diagnose this issue) - Teach instantiation of static data members to find the previous declaration, so that we build proper redeclaration chains. Previously, we had the redeclaration chain but built it too late to be useful, because... - Teach instantiation of static data member definitions not to try to check an initializer if a previous declaration already had an initializer. This makes sure that we don't complain about static const data members with in-class initializers and out-of-line definitions. - Move all of the incomplete-type checking logic out of Sema::FinalizeDeclaratorGroup; it makes more sense in ActOnUnitializedDecl. There may still be a few places where we can improve these diagnostics. I'll address that as a separate commit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95657 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
195337d2e5
Коммит
60c93c9981
|
@ -3658,24 +3658,6 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl,
|
|||
if (VarDecl *Var = dyn_cast<VarDecl>(RealDecl)) {
|
||||
QualType Type = Var->getType();
|
||||
|
||||
// Record tentative definitions.
|
||||
if (Var->isTentativeDefinitionNow())
|
||||
TentativeDefinitions.push_back(Var);
|
||||
|
||||
// C++ [dcl.init.ref]p3:
|
||||
// The initializer can be omitted for a reference only in a
|
||||
// parameter declaration (8.3.5), in the declaration of a
|
||||
// function return type, in the declaration of a class member
|
||||
// within its class declaration (9.2), and where the extern
|
||||
// specifier is explicitly used.
|
||||
if (Type->isReferenceType() && !Var->hasExternalStorage()) {
|
||||
Diag(Var->getLocation(), diag::err_reference_var_requires_init)
|
||||
<< Var->getDeclName()
|
||||
<< SourceRange(Var->getLocation(), Var->getLocation());
|
||||
Var->setInvalidDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
// C++0x [dcl.spec.auto]p3
|
||||
if (TypeContainsUndeducedAuto) {
|
||||
Diag(Var->getLocation(), diag::err_auto_var_requires_init)
|
||||
|
@ -3684,99 +3666,133 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl,
|
|||
return;
|
||||
}
|
||||
|
||||
// An array without size is an incomplete type, and there are no special
|
||||
// rules in C++ to make such a definition acceptable.
|
||||
if (getLangOptions().CPlusPlus && Type->isIncompleteArrayType() &&
|
||||
!Var->hasExternalStorage()) {
|
||||
switch (Var->isThisDeclarationADefinition()) {
|
||||
case VarDecl::Definition:
|
||||
if (!Var->isStaticDataMember() || !Var->getAnyInitializer())
|
||||
break;
|
||||
|
||||
// We have an out-of-line definition of a static data member
|
||||
// that has an in-class initializer, so we type-check this like
|
||||
// a declaration.
|
||||
//
|
||||
// Fall through
|
||||
|
||||
case VarDecl::DeclarationOnly:
|
||||
// It's only a declaration.
|
||||
|
||||
// Block scope. C99 6.7p7: If an identifier for an object is
|
||||
// declared with no linkage (C99 6.2.2p6), the type for the
|
||||
// object shall be complete.
|
||||
if (!Type->isDependentType() && Var->isBlockVarDecl() &&
|
||||
!Var->getLinkage() && !Var->isInvalidDecl() &&
|
||||
RequireCompleteType(Var->getLocation(), Type,
|
||||
diag::err_typecheck_decl_incomplete_type))
|
||||
Var->setInvalidDecl();
|
||||
|
||||
// Make sure that the type is not abstract.
|
||||
if (!Type->isDependentType() && !Var->isInvalidDecl() &&
|
||||
RequireNonAbstractType(Var->getLocation(), Type,
|
||||
diag::err_abstract_type_in_decl,
|
||||
AbstractVariableType))
|
||||
Var->setInvalidDecl();
|
||||
return;
|
||||
|
||||
case VarDecl::TentativeDefinition:
|
||||
// File scope. C99 6.9.2p2: A declaration of an identifier for an
|
||||
// object that has file scope without an initializer, and without a
|
||||
// storage-class specifier or with the storage-class specifier "static",
|
||||
// constitutes a tentative definition. Note: A tentative definition with
|
||||
// external linkage is valid (C99 6.2.2p5).
|
||||
if (!Var->isInvalidDecl()) {
|
||||
if (const IncompleteArrayType *ArrayT
|
||||
= Context.getAsIncompleteArrayType(Type)) {
|
||||
if (RequireCompleteType(Var->getLocation(),
|
||||
ArrayT->getElementType(),
|
||||
diag::err_illegal_decl_array_incomplete_type))
|
||||
Var->setInvalidDecl();
|
||||
} else if (Var->getStorageClass() == VarDecl::Static) {
|
||||
// C99 6.9.2p3: If the declaration of an identifier for an object is
|
||||
// a tentative definition and has internal linkage (C99 6.2.2p3), the
|
||||
// declared type shall not be an incomplete type.
|
||||
// NOTE: code such as the following
|
||||
// static struct s;
|
||||
// struct s { int a; };
|
||||
// is accepted by gcc. Hence here we issue a warning instead of
|
||||
// an error and we do not invalidate the static declaration.
|
||||
// NOTE: to avoid multiple warnings, only check the first declaration.
|
||||
if (Var->getPreviousDeclaration() == 0)
|
||||
RequireCompleteType(Var->getLocation(), Type,
|
||||
diag::ext_typecheck_decl_incomplete_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Record the tentative definition; we're done.
|
||||
if (!Var->isInvalidDecl())
|
||||
TentativeDefinitions.push_back(Var);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
Var->setInvalidDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ [temp.expl.spec]p15:
|
||||
// An explicit specialization of a static data member of a template is a
|
||||
// definition if the declaration includes an initializer; otherwise, it
|
||||
// is a declaration.
|
||||
if (Var->isStaticDataMember() &&
|
||||
Var->getInstantiatedFromStaticDataMember() &&
|
||||
Var->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
|
||||
// Provide a specific diagnostic for uninitialized variable
|
||||
// definitions with reference type.
|
||||
if (Type->isReferenceType()) {
|
||||
Diag(Var->getLocation(), diag::err_reference_var_requires_init)
|
||||
<< Var->getDeclName()
|
||||
<< SourceRange(Var->getLocation(), Var->getLocation());
|
||||
Var->setInvalidDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not attempt to type-check the default initializer for a
|
||||
// variable with dependent type.
|
||||
if (Type->isDependentType())
|
||||
return;
|
||||
|
||||
// C++ [dcl.init]p9:
|
||||
// If no initializer is specified for an object, and the object
|
||||
// is of (possibly cv-qualified) non-POD class type (or array
|
||||
// thereof), the object shall be default-initialized; if the
|
||||
// object is of const-qualified type, the underlying class type
|
||||
// shall have a user-declared default constructor.
|
||||
//
|
||||
// FIXME: Diagnose the "user-declared default constructor" bit.
|
||||
if (getLangOptions().CPlusPlus) {
|
||||
QualType InitType = Type;
|
||||
if (const ArrayType *Array = Context.getAsArrayType(Type))
|
||||
InitType = Context.getBaseElementType(Array);
|
||||
if ((!Var->hasExternalStorage() && !Var->isExternC()) &&
|
||||
InitType->isRecordType() && !InitType->isDependentType()) {
|
||||
if (!RequireCompleteType(Var->getLocation(), InitType,
|
||||
diag::err_invalid_incomplete_type_use)) {
|
||||
InitializedEntity Entity
|
||||
= InitializedEntity::InitializeVariable(Var);
|
||||
InitializationKind Kind
|
||||
= InitializationKind::CreateDefault(Var->getLocation());
|
||||
|
||||
InitializationSequence InitSeq(*this, Entity, Kind, 0, 0);
|
||||
OwningExprResult Init = InitSeq.Perform(*this, Entity, Kind,
|
||||
MultiExprArg(*this, 0, 0));
|
||||
if (Init.isInvalid())
|
||||
Var->setInvalidDecl();
|
||||
else {
|
||||
if (Init.get())
|
||||
Var->setInit(Context,
|
||||
MaybeCreateCXXExprWithTemporaries(Init.takeAs<Expr>()));
|
||||
FinalizeVarWithDestructor(Var, InitType->getAs<RecordType>());
|
||||
}
|
||||
} else {
|
||||
Var->setInvalidDecl();
|
||||
}
|
||||
}
|
||||
if (Var->isInvalidDecl())
|
||||
return;
|
||||
|
||||
// The variable can not have an abstract class type.
|
||||
if (RequireNonAbstractType(Var->getLocation(), Type,
|
||||
diag::err_abstract_type_in_decl,
|
||||
AbstractVariableType))
|
||||
Var->setInvalidDecl();
|
||||
if (RequireCompleteType(Var->getLocation(),
|
||||
Context.getBaseElementType(Type),
|
||||
diag::err_typecheck_decl_incomplete_type)) {
|
||||
Var->setInvalidDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// FIXME: Temporarily disabled because we are not properly parsing
|
||||
// linkage specifications on declarations, e.g.,
|
||||
//
|
||||
// extern "C" const CGPoint CGPointerZero;
|
||||
//
|
||||
// C++ [dcl.init]p9:
|
||||
//
|
||||
// If no initializer is specified for an object, and the
|
||||
// object is of (possibly cv-qualified) non-POD class type (or
|
||||
// array thereof), the object shall be default-initialized; if
|
||||
// the object is of const-qualified type, the underlying class
|
||||
// type shall have a user-declared default
|
||||
// constructor. Otherwise, if no initializer is specified for
|
||||
// an object, the object and its subobjects, if any, have an
|
||||
// indeterminate initial value; if the object or any of its
|
||||
// subobjects are of const-qualified type, the program is
|
||||
// ill-formed.
|
||||
//
|
||||
// This isn't technically an error in C, so we don't diagnose it.
|
||||
//
|
||||
// FIXME: Actually perform the POD/user-defined default
|
||||
// constructor check.
|
||||
if (getLangOptions().CPlusPlus &&
|
||||
Context.getCanonicalType(Type).isConstQualified() &&
|
||||
!Var->hasExternalStorage())
|
||||
Diag(Var->getLocation(), diag::err_const_var_requires_init)
|
||||
<< Var->getName()
|
||||
<< SourceRange(Var->getLocation(), Var->getLocation());
|
||||
#endif
|
||||
// The variable can not have an abstract class type.
|
||||
if (RequireNonAbstractType(Var->getLocation(), Type,
|
||||
diag::err_abstract_type_in_decl,
|
||||
AbstractVariableType)) {
|
||||
Var->setInvalidDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
InitializedEntity Entity = InitializedEntity::InitializeVariable(Var);
|
||||
InitializationKind Kind
|
||||
= InitializationKind::CreateDefault(Var->getLocation());
|
||||
|
||||
InitializationSequence InitSeq(*this, Entity, Kind, 0, 0);
|
||||
OwningExprResult Init = InitSeq.Perform(*this, Entity, Kind,
|
||||
MultiExprArg(*this, 0, 0));
|
||||
if (Init.isInvalid())
|
||||
Var->setInvalidDecl();
|
||||
else {
|
||||
if (Init.get())
|
||||
Var->setInit(Context,
|
||||
MaybeCreateCXXExprWithTemporaries(Init.takeAs<Expr>()));
|
||||
|
||||
if (getLangOptions().CPlusPlus)
|
||||
if (const RecordType *Record
|
||||
= Context.getBaseElementType(Type)->getAs<RecordType>())
|
||||
FinalizeVarWithDestructor(Var, Record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3792,78 +3808,6 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
|
|||
if (Decl *D = Group[i].getAs<Decl>())
|
||||
Decls.push_back(D);
|
||||
|
||||
// Perform semantic analysis that depends on having fully processed both
|
||||
// the declarator and initializer.
|
||||
for (unsigned i = 0, e = Decls.size(); i != e; ++i) {
|
||||
VarDecl *IDecl = dyn_cast<VarDecl>(Decls[i]);
|
||||
if (!IDecl)
|
||||
continue;
|
||||
QualType T = IDecl->getType();
|
||||
|
||||
// Block scope. C99 6.7p7: If an identifier for an object is declared with
|
||||
// no linkage (C99 6.2.2p6), the type for the object shall be complete...
|
||||
if (IDecl->isBlockVarDecl() && !IDecl->hasExternalStorage()) {
|
||||
if (T->isDependentType()) {
|
||||
// If T is dependent, we should not require a complete type.
|
||||
// (RequireCompleteType shouldn't be called with dependent types.)
|
||||
// But we still can at least check if we've got an array of unspecified
|
||||
// size without an initializer.
|
||||
if (!IDecl->isInvalidDecl() && T->isIncompleteArrayType() &&
|
||||
!IDecl->getInit()) {
|
||||
Diag(IDecl->getLocation(), diag::err_typecheck_decl_incomplete_type)
|
||||
<< T;
|
||||
IDecl->setInvalidDecl();
|
||||
}
|
||||
} else if (!IDecl->isInvalidDecl()) {
|
||||
// If T is an incomplete array type with an initializer list that is
|
||||
// dependent on something, its size has not been fixed. We could attempt
|
||||
// to fix the size for such arrays, but we would still have to check
|
||||
// here for initializers containing a C++0x vararg expansion, e.g.
|
||||
// template <typename... Args> void f(Args... args) {
|
||||
// int vals[] = { args };
|
||||
// }
|
||||
const IncompleteArrayType *IAT = Context.getAsIncompleteArrayType(T);
|
||||
Expr *Init = IDecl->getInit();
|
||||
if (IAT && Init &&
|
||||
(Init->isTypeDependent() || Init->isValueDependent())) {
|
||||
// Check that the member type of the array is complete, at least.
|
||||
if (RequireCompleteType(IDecl->getLocation(), IAT->getElementType(),
|
||||
diag::err_typecheck_decl_incomplete_type))
|
||||
IDecl->setInvalidDecl();
|
||||
} else if (RequireCompleteType(IDecl->getLocation(), T,
|
||||
diag::err_typecheck_decl_incomplete_type))
|
||||
IDecl->setInvalidDecl();
|
||||
}
|
||||
}
|
||||
// File scope. C99 6.9.2p2: A declaration of an identifier for an
|
||||
// object that has file scope without an initializer, and without a
|
||||
// storage-class specifier or with the storage-class specifier "static",
|
||||
// constitutes a tentative definition. Note: A tentative definition with
|
||||
// external linkage is valid (C99 6.2.2p5).
|
||||
if (IDecl->isThisDeclarationADefinition() == VarDecl::TentativeDefinition &&
|
||||
!IDecl->isInvalidDecl()) {
|
||||
if (const IncompleteArrayType *ArrayT
|
||||
= Context.getAsIncompleteArrayType(T)) {
|
||||
if (RequireCompleteType(IDecl->getLocation(),
|
||||
ArrayT->getElementType(),
|
||||
diag::err_illegal_decl_array_incomplete_type))
|
||||
IDecl->setInvalidDecl();
|
||||
} else if (IDecl->getStorageClass() == VarDecl::Static) {
|
||||
// C99 6.9.2p3: If the declaration of an identifier for an object is
|
||||
// a tentative definition and has internal linkage (C99 6.2.2p3), the
|
||||
// declared type shall not be an incomplete type.
|
||||
// NOTE: code such as the following
|
||||
// static struct s;
|
||||
// struct s { int a; };
|
||||
// is accepted by gcc. Hence here we issue a warning instead of
|
||||
// an error and we do not invalidate the static declaration.
|
||||
// NOTE: to avoid multiple warnings, only check the first declaration.
|
||||
if (IDecl->getPreviousDeclaration() == 0)
|
||||
RequireCompleteType(IDecl->getLocation(), T,
|
||||
diag::ext_typecheck_decl_incomplete_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return DeclGroupPtrTy::make(DeclGroupRef::Create(Context,
|
||||
Decls.data(), Decls.size()));
|
||||
}
|
||||
|
|
|
@ -2650,7 +2650,7 @@ static void TryDefaultInitialization(Sema &S,
|
|||
// - if T is a (possibly cv-qualified) class type (Clause 9), the default
|
||||
// constructor for T is called (and the initialization is ill-formed if
|
||||
// T has no accessible default constructor);
|
||||
if (DestType->isRecordType()) {
|
||||
if (DestType->isRecordType() && S.getLangOptions().CPlusPlus) {
|
||||
return TryConstructorInitialization(S, Entity, Kind, 0, 0, DestType,
|
||||
Sequence);
|
||||
}
|
||||
|
@ -2661,7 +2661,7 @@ static void TryDefaultInitialization(Sema &S,
|
|||
// If a program calls for the default initialization of an object of
|
||||
// a const-qualified type T, T shall be a class type with a user-provided
|
||||
// default constructor.
|
||||
if (DestType.isConstQualified())
|
||||
if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus)
|
||||
Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst);
|
||||
}
|
||||
|
||||
|
|
|
@ -217,6 +217,8 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
|
|||
// FIXME: having to fake up a LookupResult is dumb.
|
||||
LookupResult Previous(SemaRef, Var->getDeclName(), Var->getLocation(),
|
||||
Sema::LookupOrdinaryName);
|
||||
if (D->isStaticDataMember())
|
||||
SemaRef.LookupQualifiedName(Previous, Owner, false);
|
||||
SemaRef.CheckVariableDeclaration(Var, Previous, Redeclaration);
|
||||
|
||||
if (D->isOutOfLine()) {
|
||||
|
@ -232,7 +234,9 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
|
|||
SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D,
|
||||
TSK_ImplicitInstantiation);
|
||||
|
||||
if (D->getInit()) {
|
||||
if (Var->getAnyInitializer()) {
|
||||
// We already have an initializer in the class.
|
||||
} else if (D->getInit()) {
|
||||
if (Var->isStaticDataMember() && !D->isOutOfLine())
|
||||
SemaRef.PushExpressionEvaluationContext(Sema::Unevaluated);
|
||||
else
|
||||
|
@ -1821,7 +1825,6 @@ void Sema::InstantiateStaticDataMemberDefinition(
|
|||
CurContext = PreviousContext;
|
||||
|
||||
if (Var) {
|
||||
Var->setPreviousDeclaration(OldVar);
|
||||
MemberSpecializationInfo *MSInfo = OldVar->getMemberSpecializationInfo();
|
||||
assert(MSInfo && "Missing member specialization information?");
|
||||
Var->setTemplateSpecializationKind(MSInfo->getTemplateSpecializationKind(),
|
||||
|
|
|
@ -11,5 +11,5 @@ struct HasUserDefault { HasUserDefault(); };
|
|||
void test_const_default_init() {
|
||||
const NoUserDefault x1; // expected-error{{default initialization of an object of const type 'struct NoUserDefault const' requires a user-provided default constructor}}
|
||||
const HasUserDefault x2;
|
||||
const int x3; // FIXME: xpected-error{{default initialization of an object of const type 'struct NoUserDefault const' requires a user-provided default constructor}}
|
||||
const int x3; // expected-error{{default initialization of an object of const type 'int const'}}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
int a0;
|
||||
const volatile int a1;
|
||||
const volatile int a1 = 2;
|
||||
int a2[16];
|
||||
int a3();
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ template double Foo::As2();
|
|||
// Partial ordering with conversion function templates.
|
||||
struct X0 {
|
||||
template<typename T> operator T*() {
|
||||
T x;
|
||||
T x = 1;
|
||||
x = 17; // expected-error{{read-only variable is not assignable}}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ int ary[]; // expected-warning {{tentative array definition assumed to have one
|
|||
struct foo bary[]; // expected-error {{array has incomplete element type 'struct foo'}}
|
||||
|
||||
void func() {
|
||||
int ary[]; // expected-error{{variable has incomplete type 'int []'}}
|
||||
int ary[]; // expected-error{{definition of variable with array type needs an explicit size or an initializer}}
|
||||
void b; // expected-error {{variable has incomplete type 'void'}}
|
||||
struct foo f; // expected-error {{variable has incomplete type 'struct foo'}}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ X0<T> make_X0(const T &Val) {
|
|||
}
|
||||
|
||||
void test_X0() {
|
||||
const char array[2];
|
||||
const char array[2] = { 'a', 'b' };
|
||||
make_X0(array);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ void foo(double a)
|
|||
{
|
||||
S w(int(a)); // expected-warning{{disambiguated}}
|
||||
w(17);
|
||||
S x(int()); // expected-warning{{disambiguated}}
|
||||
x(&returns_an_int);
|
||||
S x1(int()); // expected-warning{{disambiguated}}
|
||||
x1(&returns_an_int);
|
||||
S y((int)a);
|
||||
y.bar();
|
||||
S z = int(a);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
template<int i> struct x {
|
||||
static const int j = i;
|
||||
x<j>* y;
|
||||
|
@ -10,7 +9,6 @@ const int x<i>::j;
|
|||
|
||||
int array0[x<2>::j];
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct X0 {
|
||||
static const unsigned value = sizeof(T);
|
||||
|
|
|
@ -173,8 +173,8 @@ struct is_pod {
|
|||
static const bool value = __is_pod(T);
|
||||
};
|
||||
|
||||
static const int is_pod0[is_pod<X>::value? -1 : 1];
|
||||
static const int is_pod1[is_pod<Y>::value? 1 : -1];
|
||||
static int is_pod0[is_pod<X>::value? -1 : 1];
|
||||
static int is_pod1[is_pod<Y>::value? 1 : -1];
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// initializer lists
|
||||
|
|
Загрузка…
Ссылка в новой задаче