Skip to content

Commit b655d75

Browse files
sdkrystianronlieb
authored andcommitted
[Clang][Sema] Diagnose current instantiation used as an incomplete base class (llvm#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). Change-Id: I548ebcd030ada0e7a297af7f1aecb41ea8c6b102
1 parent 0d1b252 commit b655d75

File tree

13 files changed

+262
-185
lines changed

13 files changed

+262
-185
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/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9531,8 +9531,6 @@ def err_static_data_member_not_allowed_in_local_class : Error<
95319531
def err_base_clause_on_union : Error<"unions cannot have base classes">;
95329532
def err_base_must_be_class : Error<"base specifier must name a class">;
95339533
def err_union_as_base_class : Error<"unions cannot be base classes">;
9534-
def err_circular_inheritance : Error<
9535-
"circular inheritance between %0 and %1">;
95369534
def err_base_class_has_flexible_array_member : Error<
95379535
"base class %0 has a flexible array member">;
95389536
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
@@ -2382,6 +2382,14 @@ bool Type::isIncompleteType(NamedDecl **Def) const {
23822382
*Def = Rec;
23832383
return !Rec->isCompleteDefinition();
23842384
}
2385+
case InjectedClassName: {
2386+
CXXRecordDecl *Rec = cast<InjectedClassNameType>(CanonicalType)->getDecl();
2387+
if (!Rec->isBeingDefined())
2388+
return false;
2389+
if (Def)
2390+
*Def = Rec;
2391+
return true;
2392+
}
23852393
case ConstantArray:
23862394
case VariableArray:
23872395
// 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
@@ -2631,188 +2631,122 @@ bool Sema::isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS) {
26312631
return false;
26322632
}
26332633

2634-
/// Determine whether the given class is a base class of the given
2635-
/// class, including looking at dependent bases.
2636-
static bool findCircularInheritance(const CXXRecordDecl *Class,
2637-
const CXXRecordDecl *Current) {
2638-
SmallVector<const CXXRecordDecl*, 8> Queue;
2639-
2640-
Class = Class->getCanonicalDecl();
2641-
while (true) {
2642-
for (const auto &I : Current->bases()) {
2643-
CXXRecordDecl *Base = I.getType()->getAsCXXRecordDecl();
2644-
if (!Base)
2645-
continue;
2646-
2647-
Base = Base->getDefinition();
2648-
if (!Base)
2649-
continue;
2650-
2651-
if (Base->getCanonicalDecl() == Class)
2652-
return true;
2653-
2654-
Queue.push_back(Base);
2655-
}
2656-
2657-
if (Queue.empty())
2658-
return false;
2659-
2660-
Current = Queue.pop_back_val();
2661-
}
2662-
2663-
return false;
2664-
}
2665-
26662634
/// Check the validity of a C++ base class specifier.
26672635
///
26682636
/// \returns a new CXXBaseSpecifier if well-formed, emits diagnostics
26692637
/// and returns NULL otherwise.
2670-
CXXBaseSpecifier *
2671-
Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
2672-
SourceRange SpecifierRange,
2673-
bool Virtual, AccessSpecifier Access,
2674-
TypeSourceInfo *TInfo,
2675-
SourceLocation EllipsisLoc) {
2676-
// In HLSL, unspecified class access is public rather than private.
2677-
if (getLangOpts().HLSL && Class->getTagKind() == TagTypeKind::Class &&
2678-
Access == AS_none)
2679-
Access = AS_public;
2680-
2638+
CXXBaseSpecifier *Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
2639+
SourceRange SpecifierRange,
2640+
bool Virtual, AccessSpecifier Access,
2641+
TypeSourceInfo *TInfo,
2642+
SourceLocation EllipsisLoc) {
26812643
QualType BaseType = TInfo->getType();
2644+
SourceLocation BaseLoc = TInfo->getTypeLoc().getBeginLoc();
26822645
if (BaseType->containsErrors()) {
26832646
// Already emitted a diagnostic when parsing the error type.
26842647
return nullptr;
26852648
}
2686-
// C++ [class.union]p1:
2687-
// A union shall not have base classes.
2688-
if (Class->isUnion()) {
2689-
Diag(Class->getLocation(), diag::err_base_clause_on_union)
2690-
<< SpecifierRange;
2691-
return nullptr;
2692-
}
26932649

2694-
if (EllipsisLoc.isValid() &&
2695-
!TInfo->getType()->containsUnexpandedParameterPack()) {
2650+
if (EllipsisLoc.isValid() && !BaseType->containsUnexpandedParameterPack()) {
26962651
Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
26972652
<< TInfo->getTypeLoc().getSourceRange();
26982653
EllipsisLoc = SourceLocation();
26992654
}
27002655

2701-
SourceLocation BaseLoc = TInfo->getTypeLoc().getBeginLoc();
2702-
2703-
if (BaseType->isDependentType()) {
2704-
// Make sure that we don't have circular inheritance among our dependent
2705-
// bases. For non-dependent bases, the check for completeness below handles
2706-
// this.
2707-
if (CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl()) {
2708-
if (BaseDecl->getCanonicalDecl() == Class->getCanonicalDecl() ||
2709-
((BaseDecl = BaseDecl->getDefinition()) &&
2710-
findCircularInheritance(Class, BaseDecl))) {
2711-
Diag(BaseLoc, diag::err_circular_inheritance)
2712-
<< BaseType << Context.getTypeDeclType(Class);
2713-
2714-
if (BaseDecl->getCanonicalDecl() != Class->getCanonicalDecl())
2715-
Diag(BaseDecl->getLocation(), diag::note_previous_decl)
2716-
<< BaseType;
2656+
auto *BaseDecl =
2657+
dyn_cast_if_present<CXXRecordDecl>(computeDeclContext(BaseType));
2658+
// C++ [class.derived.general]p2:
2659+
// A class-or-decltype shall denote a (possibly cv-qualified) class type
2660+
// that is not an incompletely defined class; any cv-qualifiers are
2661+
// ignored.
2662+
if (BaseDecl) {
2663+
// C++ [class.union.general]p4:
2664+
// [...] A union shall not be used as a base class.
2665+
if (BaseDecl->isUnion()) {
2666+
Diag(BaseLoc, diag::err_union_as_base_class) << SpecifierRange;
2667+
return nullptr;
2668+
}
27172669

2718-
return nullptr;
2670+
// For the MS ABI, propagate DLL attributes to base class templates.
2671+
if (Context.getTargetInfo().getCXXABI().isMicrosoft() ||
2672+
Context.getTargetInfo().getTriple().isPS()) {
2673+
if (Attr *ClassAttr = getDLLAttr(Class)) {
2674+
if (auto *BaseSpec =
2675+
dyn_cast<ClassTemplateSpecializationDecl>(BaseDecl)) {
2676+
propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseSpec,
2677+
BaseLoc);
2678+
}
27192679
}
27202680
}
27212681

2682+
if (RequireCompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class,
2683+
SpecifierRange)) {
2684+
Class->setInvalidDecl();
2685+
return nullptr;
2686+
}
2687+
2688+
BaseDecl = BaseDecl->getDefinition();
2689+
assert(BaseDecl && "Base type is not incomplete, but has no definition");
2690+
2691+
// Microsoft docs say:
2692+
// "If a base-class has a code_seg attribute, derived classes must have the
2693+
// same attribute."
2694+
const auto *BaseCSA = BaseDecl->getAttr<CodeSegAttr>();
2695+
const auto *DerivedCSA = Class->getAttr<CodeSegAttr>();
2696+
if ((DerivedCSA || BaseCSA) &&
2697+
(!BaseCSA || !DerivedCSA ||
2698+
BaseCSA->getName() != DerivedCSA->getName())) {
2699+
Diag(Class->getLocation(), diag::err_mismatched_code_seg_base);
2700+
Diag(BaseDecl->getLocation(), diag::note_base_class_specified_here)
2701+
<< BaseDecl;
2702+
return nullptr;
2703+
}
2704+
2705+
// A class which contains a flexible array member is not suitable for use as
2706+
// a base class:
2707+
// - If the layout determines that a base comes before another base,
2708+
// the flexible array member would index into the subsequent base.
2709+
// - If the layout determines that base comes before the derived class,
2710+
// the flexible array member would index into the derived class.
2711+
if (BaseDecl->hasFlexibleArrayMember()) {
2712+
Diag(BaseLoc, diag::err_base_class_has_flexible_array_member)
2713+
<< BaseDecl->getDeclName();
2714+
return nullptr;
2715+
}
2716+
2717+
// C++ [class]p3:
2718+
// If a class is marked final and it appears as a base-type-specifier in
2719+
// base-clause, the program is ill-formed.
2720+
if (FinalAttr *FA = BaseDecl->getAttr<FinalAttr>()) {
2721+
Diag(BaseLoc, diag::err_class_marked_final_used_as_base)
2722+
<< BaseDecl->getDeclName() << FA->isSpelledAsSealed();
2723+
Diag(BaseDecl->getLocation(), diag::note_entity_declared_at)
2724+
<< BaseDecl->getDeclName() << FA->getRange();
2725+
return nullptr;
2726+
}
2727+
2728+
// If the base class is invalid the derived class is as well.
2729+
if (BaseDecl->isInvalidDecl())
2730+
Class->setInvalidDecl();
2731+
} else if (BaseType->isDependentType()) {
27222732
// Make sure that we don't make an ill-formed AST where the type of the
27232733
// Class is non-dependent and its attached base class specifier is an
27242734
// dependent type, which violates invariants in many clang code paths (e.g.
27252735
// constexpr evaluator). If this case happens (in errory-recovery mode), we
27262736
// explicitly mark the Class decl invalid. The diagnostic was already
27272737
// emitted.
2728-
if (!Class->getTypeForDecl()->isDependentType())
2738+
if (!Class->isDependentContext())
27292739
Class->setInvalidDecl();
2730-
return new (Context) CXXBaseSpecifier(
2731-
SpecifierRange, Virtual, Class->getTagKind() == TagTypeKind::Class,
2732-
Access, TInfo, EllipsisLoc);
2733-
}
2734-
2735-
// Base specifiers must be record types.
2736-
if (!BaseType->isRecordType()) {
2740+
} else {
2741+
// The base class is some non-dependent non-class type.
27372742
Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange;
27382743
return nullptr;
27392744
}
27402745

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

28172751
// Create the base specifier.
28182752
return new (Context) CXXBaseSpecifier(
@@ -2857,13 +2791,20 @@ BaseResult Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange,
28572791
UPPC_BaseType))
28582792
return true;
28592793

2794+
// C++ [class.union.general]p4:
2795+
// [...] A union shall not have base classes.
2796+
if (Class->isUnion()) {
2797+
Diag(Class->getLocation(), diag::err_base_clause_on_union)
2798+
<< SpecifierRange;
2799+
return true;
2800+
}
2801+
28602802
if (CXXBaseSpecifier *BaseSpec = CheckBaseSpecifier(Class, SpecifierRange,
28612803
Virtual, Access, TInfo,
28622804
EllipsisLoc))
28632805
return BaseSpec;
2864-
else
2865-
Class->setInvalidDecl();
28662806

2807+
Class->setInvalidDecl();
28672808
return true;
28682809
}
28692810

clang/test/AST/HLSL/this-reference-template.hlsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void main() {
2424
// CHECK: -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:8:3, line:10:3> line:8:5 getFirst 'K ()' implicit-inline
2525
// CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:16, line:10:3>
2626
// CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:9:4, col:16>
27-
// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'K' lvalue .Firstt 0x{{[0-9A-Fa-f]+}}
27+
// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'K' lvalue .First 0x{{[0-9A-Fa-f]+}}
2828
// CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:11> 'Pair<K, V>' lvalue this
2929
// CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:12:3, line:14:3> line:12:5 getSecond 'V ()' implicit-inline
3030
// CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:17, line:14:3>
@@ -43,4 +43,4 @@ void main() {
4343
// CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:13:5, col:12>
4444
// CHECK-NEXT:-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'float' <LValueToRValue>
4545
// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'float' lvalue .Second 0x{{[0-9A-Fa-f]+}}
46-
// CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'Pair<int, float>' lvalue implicit this
46+
// CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'Pair<int, float>' lvalue implicit this

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)