Skip to content

Commit acf5ad2

Browse files
authored
[Clang][Sema] Diagnose current instantiation used as an incomplete base class (#92597)
Consider the following: ``` template<typename T> struct A { struct B : A { }; }; ``` According to [class.derived.general] p2: > [...] A _class-or-decltype_ shall denote a (possibly cv-qualified) class type that is not an incompletely defined class; any cv-qualifiers are ignored. [...] Although GCC and EDG rejects this, Clang accepts it. This is incorrect, as `A` is incomplete within its own definition (outside of a complete-class context). This patch correctly diagnoses instances where the current instantiation is used as a base class before it is complete. Conversely, Clang erroneously rejects the following: ``` template<typename T> struct A { struct B; struct C : B { }; struct B : C { }; // error: circular inheritance between 'C' and 'A::B' }; ``` Though it may seem like no valid specialization of this template can be instantiated, an explicit specialization of either member classes for an implicit instantiated specialization of `A` would permit the definition of the other member class to be instantiated, e.g.: ``` template<> struct A<int>::B { }; A<int>::C c; // ok ``` So this patch also does away with this error. This means that circular inheritance is diagnosed during instantiation of the definition as a consequence of requiring the base class type to be complete (matching the behavior of GCC and EDG).
1 parent bce3680 commit acf5ad2

File tree

11 files changed

+256
-179
lines changed

11 files changed

+256
-179
lines changed

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,11 @@ struct X {
55
// CHECK-MESSAGES: :[[@LINE-1]]:5: error: field has incomplete type 'X' [clang-diagnostic-error]
66
int a = 10;
77
};
8+
9+
template <typename T> class NoCrash {
10+
// CHECK-MESSAGES: :[[@LINE+2]]:20: error: base class has incomplete type
11+
// CHECK-MESSAGES: :[[@LINE-2]]:29: note: definition of 'NoCrash<T>' is not complete until the closing '}'
12+
class B : public NoCrash {
13+
template <typename U> B(U u) {}
14+
};
15+
};

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -463,12 +463,6 @@ struct NegativeIncompleteArrayMember {
463463
char e[];
464464
};
465465

466-
template <typename T> class NoCrash {
467-
class B : public NoCrash {
468-
template <typename U> B(U u) {}
469-
};
470-
};
471-
472466
struct PositiveBitfieldMember {
473467
PositiveBitfieldMember() {}
474468
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ Bug Fixes to C++ Support
746746
the ``constexpr`` specifier. Fixes (#GH61004).
747747
- Clang no longer transforms dependent qualified names into implicit class member access expressions
748748
until it can be determined whether the name is that of a non-static member.
749+
- Clang now correctly diagnoses when the current instantiation is used as an incomplete base class.
749750

750751
Bug Fixes to AST Handling
751752
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9464,8 +9464,6 @@ def err_static_data_member_not_allowed_in_local_class : Error<
94649464
def err_base_clause_on_union : Error<"unions cannot have base classes">;
94659465
def err_base_must_be_class : Error<"base specifier must name a class">;
94669466
def err_union_as_base_class : Error<"unions cannot be base classes">;
9467-
def err_circular_inheritance : Error<
9468-
"circular inheritance between %0 and %1">;
94699467
def err_base_class_has_flexible_array_member : Error<
94709468
"base class %0 has a flexible array member">;
94719469
def err_incomplete_base_class : Error<"base class has incomplete type">;

clang/lib/AST/Type.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2372,6 +2372,14 @@ bool Type::isIncompleteType(NamedDecl **Def) const {
23722372
*Def = Rec;
23732373
return !Rec->isCompleteDefinition();
23742374
}
2375+
case InjectedClassName: {
2376+
CXXRecordDecl *Rec = cast<InjectedClassNameType>(CanonicalType)->getDecl();
2377+
if (!Rec->isBeingDefined())
2378+
return false;
2379+
if (Def)
2380+
*Def = Rec;
2381+
return true;
2382+
}
23752383
case ConstantArray:
23762384
case VariableArray:
23772385
// An array is incomplete if its element type is incomplete

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 95 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -2656,188 +2656,122 @@ bool Sema::isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS) {
26562656
return false;
26572657
}
26582658

2659-
/// Determine whether the given class is a base class of the given
2660-
/// class, including looking at dependent bases.
2661-
static bool findCircularInheritance(const CXXRecordDecl *Class,
2662-
const CXXRecordDecl *Current) {
2663-
SmallVector<const CXXRecordDecl*, 8> Queue;
2664-
2665-
Class = Class->getCanonicalDecl();
2666-
while (true) {
2667-
for (const auto &I : Current->bases()) {
2668-
CXXRecordDecl *Base = I.getType()->getAsCXXRecordDecl();
2669-
if (!Base)
2670-
continue;
2671-
2672-
Base = Base->getDefinition();
2673-
if (!Base)
2674-
continue;
2675-
2676-
if (Base->getCanonicalDecl() == Class)
2677-
return true;
2678-
2679-
Queue.push_back(Base);
2680-
}
2681-
2682-
if (Queue.empty())
2683-
return false;
2684-
2685-
Current = Queue.pop_back_val();
2686-
}
2687-
2688-
return false;
2689-
}
2690-
26912659
/// Check the validity of a C++ base class specifier.
26922660
///
26932661
/// \returns a new CXXBaseSpecifier if well-formed, emits diagnostics
26942662
/// and returns NULL otherwise.
2695-
CXXBaseSpecifier *
2696-
Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
2697-
SourceRange SpecifierRange,
2698-
bool Virtual, AccessSpecifier Access,
2699-
TypeSourceInfo *TInfo,
2700-
SourceLocation EllipsisLoc) {
2701-
// In HLSL, unspecified class access is public rather than private.
2702-
if (getLangOpts().HLSL && Class->getTagKind() == TagTypeKind::Class &&
2703-
Access == AS_none)
2704-
Access = AS_public;
2705-
2663+
CXXBaseSpecifier *Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
2664+
SourceRange SpecifierRange,
2665+
bool Virtual, AccessSpecifier Access,
2666+
TypeSourceInfo *TInfo,
2667+
SourceLocation EllipsisLoc) {
27062668
QualType BaseType = TInfo->getType();
2669+
SourceLocation BaseLoc = TInfo->getTypeLoc().getBeginLoc();
27072670
if (BaseType->containsErrors()) {
27082671
// Already emitted a diagnostic when parsing the error type.
27092672
return nullptr;
27102673
}
2711-
// C++ [class.union]p1:
2712-
// A union shall not have base classes.
2713-
if (Class->isUnion()) {
2714-
Diag(Class->getLocation(), diag::err_base_clause_on_union)
2715-
<< SpecifierRange;
2716-
return nullptr;
2717-
}
27182674

2719-
if (EllipsisLoc.isValid() &&
2720-
!TInfo->getType()->containsUnexpandedParameterPack()) {
2675+
if (EllipsisLoc.isValid() && !BaseType->containsUnexpandedParameterPack()) {
27212676
Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
27222677
<< TInfo->getTypeLoc().getSourceRange();
27232678
EllipsisLoc = SourceLocation();
27242679
}
27252680

2726-
SourceLocation BaseLoc = TInfo->getTypeLoc().getBeginLoc();
2727-
2728-
if (BaseType->isDependentType()) {
2729-
// Make sure that we don't have circular inheritance among our dependent
2730-
// bases. For non-dependent bases, the check for completeness below handles
2731-
// this.
2732-
if (CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl()) {
2733-
if (BaseDecl->getCanonicalDecl() == Class->getCanonicalDecl() ||
2734-
((BaseDecl = BaseDecl->getDefinition()) &&
2735-
findCircularInheritance(Class, BaseDecl))) {
2736-
Diag(BaseLoc, diag::err_circular_inheritance)
2737-
<< BaseType << Context.getTypeDeclType(Class);
2738-
2739-
if (BaseDecl->getCanonicalDecl() != Class->getCanonicalDecl())
2740-
Diag(BaseDecl->getLocation(), diag::note_previous_decl)
2741-
<< BaseType;
2681+
auto *BaseDecl =
2682+
dyn_cast_if_present<CXXRecordDecl>(computeDeclContext(BaseType));
2683+
// C++ [class.derived.general]p2:
2684+
// A class-or-decltype shall denote a (possibly cv-qualified) class type
2685+
// that is not an incompletely defined class; any cv-qualifiers are
2686+
// ignored.
2687+
if (BaseDecl) {
2688+
// C++ [class.union.general]p4:
2689+
// [...] A union shall not be used as a base class.
2690+
if (BaseDecl->isUnion()) {
2691+
Diag(BaseLoc, diag::err_union_as_base_class) << SpecifierRange;
2692+
return nullptr;
2693+
}
27422694

2743-
return nullptr;
2695+
// For the MS ABI, propagate DLL attributes to base class templates.
2696+
if (Context.getTargetInfo().getCXXABI().isMicrosoft() ||
2697+
Context.getTargetInfo().getTriple().isPS()) {
2698+
if (Attr *ClassAttr = getDLLAttr(Class)) {
2699+
if (auto *BaseSpec =
2700+
dyn_cast<ClassTemplateSpecializationDecl>(BaseDecl)) {
2701+
propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseSpec,
2702+
BaseLoc);
2703+
}
27442704
}
27452705
}
27462706

2707+
if (RequireCompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class,
2708+
SpecifierRange)) {
2709+
Class->setInvalidDecl();
2710+
return nullptr;
2711+
}
2712+
2713+
BaseDecl = BaseDecl->getDefinition();
2714+
assert(BaseDecl && "Base type is not incomplete, but has no definition");
2715+
2716+
// Microsoft docs say:
2717+
// "If a base-class has a code_seg attribute, derived classes must have the
2718+
// same attribute."
2719+
const auto *BaseCSA = BaseDecl->getAttr<CodeSegAttr>();
2720+
const auto *DerivedCSA = Class->getAttr<CodeSegAttr>();
2721+
if ((DerivedCSA || BaseCSA) &&
2722+
(!BaseCSA || !DerivedCSA ||
2723+
BaseCSA->getName() != DerivedCSA->getName())) {
2724+
Diag(Class->getLocation(), diag::err_mismatched_code_seg_base);
2725+
Diag(BaseDecl->getLocation(), diag::note_base_class_specified_here)
2726+
<< BaseDecl;
2727+
return nullptr;
2728+
}
2729+
2730+
// A class which contains a flexible array member is not suitable for use as
2731+
// a base class:
2732+
// - If the layout determines that a base comes before another base,
2733+
// the flexible array member would index into the subsequent base.
2734+
// - If the layout determines that base comes before the derived class,
2735+
// the flexible array member would index into the derived class.
2736+
if (BaseDecl->hasFlexibleArrayMember()) {
2737+
Diag(BaseLoc, diag::err_base_class_has_flexible_array_member)
2738+
<< BaseDecl->getDeclName();
2739+
return nullptr;
2740+
}
2741+
2742+
// C++ [class]p3:
2743+
// If a class is marked final and it appears as a base-type-specifier in
2744+
// base-clause, the program is ill-formed.
2745+
if (FinalAttr *FA = BaseDecl->getAttr<FinalAttr>()) {
2746+
Diag(BaseLoc, diag::err_class_marked_final_used_as_base)
2747+
<< BaseDecl->getDeclName() << FA->isSpelledAsSealed();
2748+
Diag(BaseDecl->getLocation(), diag::note_entity_declared_at)
2749+
<< BaseDecl->getDeclName() << FA->getRange();
2750+
return nullptr;
2751+
}
2752+
2753+
// If the base class is invalid the derived class is as well.
2754+
if (BaseDecl->isInvalidDecl())
2755+
Class->setInvalidDecl();
2756+
} else if (BaseType->isDependentType()) {
27472757
// Make sure that we don't make an ill-formed AST where the type of the
27482758
// Class is non-dependent and its attached base class specifier is an
27492759
// dependent type, which violates invariants in many clang code paths (e.g.
27502760
// constexpr evaluator). If this case happens (in errory-recovery mode), we
27512761
// explicitly mark the Class decl invalid. The diagnostic was already
27522762
// emitted.
2753-
if (!Class->getTypeForDecl()->isDependentType())
2763+
if (!Class->isDependentContext())
27542764
Class->setInvalidDecl();
2755-
return new (Context) CXXBaseSpecifier(
2756-
SpecifierRange, Virtual, Class->getTagKind() == TagTypeKind::Class,
2757-
Access, TInfo, EllipsisLoc);
2758-
}
2759-
2760-
// Base specifiers must be record types.
2761-
if (!BaseType->isRecordType()) {
2765+
} else {
2766+
// The base class is some non-dependent non-class type.
27622767
Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange;
27632768
return nullptr;
27642769
}
27652770

2766-
// C++ [class.union]p1:
2767-
// A union shall not be used as a base class.
2768-
if (BaseType->isUnionType()) {
2769-
Diag(BaseLoc, diag::err_union_as_base_class) << SpecifierRange;
2770-
return nullptr;
2771-
}
2772-
2773-
// For the MS ABI, propagate DLL attributes to base class templates.
2774-
if (Context.getTargetInfo().getCXXABI().isMicrosoft() ||
2775-
Context.getTargetInfo().getTriple().isPS()) {
2776-
if (Attr *ClassAttr = getDLLAttr(Class)) {
2777-
if (auto *BaseTemplate = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
2778-
BaseType->getAsCXXRecordDecl())) {
2779-
propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseTemplate,
2780-
BaseLoc);
2781-
}
2782-
}
2783-
}
2784-
2785-
// C++ [class.derived]p2:
2786-
// The class-name in a base-specifier shall not be an incompletely
2787-
// defined class.
2788-
if (RequireCompleteType(BaseLoc, BaseType,
2789-
diag::err_incomplete_base_class, SpecifierRange)) {
2790-
Class->setInvalidDecl();
2791-
return nullptr;
2792-
}
2793-
2794-
// If the base class is polymorphic or isn't empty, the new one is/isn't, too.
2795-
RecordDecl *BaseDecl = BaseType->castAs<RecordType>()->getDecl();
2796-
assert(BaseDecl && "Record type has no declaration");
2797-
BaseDecl = BaseDecl->getDefinition();
2798-
assert(BaseDecl && "Base type is not incomplete, but has no definition");
2799-
CXXRecordDecl *CXXBaseDecl = cast<CXXRecordDecl>(BaseDecl);
2800-
assert(CXXBaseDecl && "Base type is not a C++ type");
2801-
2802-
// Microsoft docs say:
2803-
// "If a base-class has a code_seg attribute, derived classes must have the
2804-
// same attribute."
2805-
const auto *BaseCSA = CXXBaseDecl->getAttr<CodeSegAttr>();
2806-
const auto *DerivedCSA = Class->getAttr<CodeSegAttr>();
2807-
if ((DerivedCSA || BaseCSA) &&
2808-
(!BaseCSA || !DerivedCSA || BaseCSA->getName() != DerivedCSA->getName())) {
2809-
Diag(Class->getLocation(), diag::err_mismatched_code_seg_base);
2810-
Diag(CXXBaseDecl->getLocation(), diag::note_base_class_specified_here)
2811-
<< CXXBaseDecl;
2812-
return nullptr;
2813-
}
2814-
2815-
// A class which contains a flexible array member is not suitable for use as a
2816-
// base class:
2817-
// - If the layout determines that a base comes before another base,
2818-
// the flexible array member would index into the subsequent base.
2819-
// - If the layout determines that base comes before the derived class,
2820-
// the flexible array member would index into the derived class.
2821-
if (CXXBaseDecl->hasFlexibleArrayMember()) {
2822-
Diag(BaseLoc, diag::err_base_class_has_flexible_array_member)
2823-
<< CXXBaseDecl->getDeclName();
2824-
return nullptr;
2825-
}
2826-
2827-
// C++ [class]p3:
2828-
// If a class is marked final and it appears as a base-type-specifier in
2829-
// base-clause, the program is ill-formed.
2830-
if (FinalAttr *FA = CXXBaseDecl->getAttr<FinalAttr>()) {
2831-
Diag(BaseLoc, diag::err_class_marked_final_used_as_base)
2832-
<< CXXBaseDecl->getDeclName()
2833-
<< FA->isSpelledAsSealed();
2834-
Diag(CXXBaseDecl->getLocation(), diag::note_entity_declared_at)
2835-
<< CXXBaseDecl->getDeclName() << FA->getRange();
2836-
return nullptr;
2837-
}
2838-
2839-
if (BaseDecl->isInvalidDecl())
2840-
Class->setInvalidDecl();
2771+
// In HLSL, unspecified class access is public rather than private.
2772+
if (getLangOpts().HLSL && Class->getTagKind() == TagTypeKind::Class &&
2773+
Access == AS_none)
2774+
Access = AS_public;
28412775

28422776
// Create the base specifier.
28432777
return new (Context) CXXBaseSpecifier(
@@ -2887,13 +2821,20 @@ BaseResult Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange,
28872821
UPPC_BaseType))
28882822
return true;
28892823

2824+
// C++ [class.union.general]p4:
2825+
// [...] A union shall not have base classes.
2826+
if (Class->isUnion()) {
2827+
Diag(Class->getLocation(), diag::err_base_clause_on_union)
2828+
<< SpecifierRange;
2829+
return true;
2830+
}
2831+
28902832
if (CXXBaseSpecifier *BaseSpec = CheckBaseSpecifier(Class, SpecifierRange,
28912833
Virtual, Access, TInfo,
28922834
EllipsisLoc))
28932835
return BaseSpec;
2894-
else
2895-
Class->setInvalidDecl();
28962836

2837+
Class->setInvalidDecl();
28972838
return true;
28982839
}
28992840

clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,15 @@ namespace InhCtor {
141141
// ill-formed.
142142
template<typename T>
143143
struct S : T {
144-
struct U : S { // expected-note 6{{candidate}}
145-
using S::S;
146-
};
144+
struct U; // expected-note 6{{candidate}}
147145
using T::T;
148146
};
147+
148+
template<typename T>
149+
struct S<T>::U : S {
150+
using S::S;
151+
};
152+
149153
S<A>::U ua(0); // expected-error {{no match}}
150154
S<B>::U ub(0); // expected-error {{no match}}
151155

0 commit comments

Comments
 (0)