https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/92597
>From 4a535c2f2660583487018f421788cd2b88d8428d Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 17 May 2024 13:30:04 -0400 Subject: [PATCH 1/7] [Clang][Sema] Diagnose current instantiation used a incomplete base class --- clang/lib/AST/Type.cpp | 4 + clang/lib/Sema/SemaDeclCXX.cpp | 116 +++++++++--------- .../basic.lookup.qual/class.qual/p2.cpp | 10 +- clang/test/SemaTemplate/dependent-names.cpp | 9 +- .../test/SemaTemplate/destructor-template.cpp | 14 ++- .../test/SemaTemplate/typo-dependent-name.cpp | 7 +- 6 files changed, 84 insertions(+), 76 deletions(-) diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index e31741cd44240..4ad764b67f81f 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2372,6 +2372,10 @@ bool Type::isIncompleteType(NamedDecl **Def) const { *Def = Rec; return !Rec->isCompleteDefinition(); } + case InjectedClassName: { + CXXRecordDecl *Rec = cast<InjectedClassNameType>(CanonicalType)->getDecl(); + return Rec->isBeingDefined(); + } case ConstantArray: case VariableArray: // An array is incomplete if its element type is incomplete diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 8225381985052..84033a602f131 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2704,28 +2704,53 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, Access = AS_public; QualType BaseType = TInfo->getType(); + SourceLocation BaseLoc = TInfo->getTypeLoc().getBeginLoc(); if (BaseType->containsErrors()) { // Already emitted a diagnostic when parsing the error type. return nullptr; } - // C++ [class.union]p1: - // A union shall not have base classes. - if (Class->isUnion()) { - Diag(Class->getLocation(), diag::err_base_clause_on_union) - << SpecifierRange; - return nullptr; - } - if (EllipsisLoc.isValid() && - !TInfo->getType()->containsUnexpandedParameterPack()) { + if (EllipsisLoc.isValid() && !BaseType->containsUnexpandedParameterPack()) { Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) << TInfo->getTypeLoc().getSourceRange(); EllipsisLoc = SourceLocation(); } - SourceLocation BaseLoc = TInfo->getTypeLoc().getBeginLoc(); + auto *BaseDecl = + dyn_cast_if_present<CXXRecordDecl>(computeDeclContext(BaseType)); + // C++ [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. + if (BaseDecl) { + // C++ [class.union.general]p4: + // [...] A union shall not be used as a base class. + if (BaseDecl->isUnion()) { + Diag(BaseLoc, diag::err_union_as_base_class) << SpecifierRange; + return nullptr; + } + + // For the MS ABI, propagate DLL attributes to base class templates. + if (Context.getTargetInfo().getCXXABI().isMicrosoft() || + Context.getTargetInfo().getTriple().isPS()) { + if (Attr *ClassAttr = getDLLAttr(Class)) { + if (auto *BaseSpec = + dyn_cast<ClassTemplateSpecializationDecl>(BaseDecl)) { + propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseSpec, + BaseLoc); + } + } + } - if (BaseType->isDependentType()) { + if (RequireCompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class, + SpecifierRange)) { + Class->setInvalidDecl(); + return nullptr; + } + } else if (!BaseType->isDependentType()) { + Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange; + return nullptr; + } else { // Make sure that we don't have circular inheritance among our dependent // bases. For non-dependent bases, the check for completeness below handles // this. @@ -2750,65 +2775,28 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // constexpr evaluator). If this case happens (in errory-recovery mode), we // explicitly mark the Class decl invalid. The diagnostic was already // emitted. - if (!Class->getTypeForDecl()->isDependentType()) + if (!Class->isDependentContext()) Class->setInvalidDecl(); return new (Context) CXXBaseSpecifier( SpecifierRange, Virtual, Class->getTagKind() == TagTypeKind::Class, Access, TInfo, EllipsisLoc); } - // Base specifiers must be record types. - if (!BaseType->isRecordType()) { - Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange; - return nullptr; - } - - // C++ [class.union]p1: - // A union shall not be used as a base class. - if (BaseType->isUnionType()) { - Diag(BaseLoc, diag::err_union_as_base_class) << SpecifierRange; - return nullptr; - } - - // For the MS ABI, propagate DLL attributes to base class templates. - if (Context.getTargetInfo().getCXXABI().isMicrosoft() || - Context.getTargetInfo().getTriple().isPS()) { - if (Attr *ClassAttr = getDLLAttr(Class)) { - if (auto *BaseTemplate = dyn_cast_or_null<ClassTemplateSpecializationDecl>( - BaseType->getAsCXXRecordDecl())) { - propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseTemplate, - BaseLoc); - } - } - } - - // C++ [class.derived]p2: - // The class-name in a base-specifier shall not be an incompletely - // defined class. - if (RequireCompleteType(BaseLoc, BaseType, - diag::err_incomplete_base_class, SpecifierRange)) { - Class->setInvalidDecl(); - return nullptr; - } - // If the base class is polymorphic or isn't empty, the new one is/isn't, too. - RecordDecl *BaseDecl = BaseType->castAs<RecordType>()->getDecl(); assert(BaseDecl && "Record type has no declaration"); BaseDecl = BaseDecl->getDefinition(); assert(BaseDecl && "Base type is not incomplete, but has no definition"); - CXXRecordDecl *CXXBaseDecl = cast<CXXRecordDecl>(BaseDecl); - assert(CXXBaseDecl && "Base type is not a C++ type"); // Microsoft docs say: // "If a base-class has a code_seg attribute, derived classes must have the // same attribute." - const auto *BaseCSA = CXXBaseDecl->getAttr<CodeSegAttr>(); + const auto *BaseCSA = BaseDecl->getAttr<CodeSegAttr>(); const auto *DerivedCSA = Class->getAttr<CodeSegAttr>(); if ((DerivedCSA || BaseCSA) && (!BaseCSA || !DerivedCSA || BaseCSA->getName() != DerivedCSA->getName())) { Diag(Class->getLocation(), diag::err_mismatched_code_seg_base); - Diag(CXXBaseDecl->getLocation(), diag::note_base_class_specified_here) - << CXXBaseDecl; + Diag(BaseDecl->getLocation(), diag::note_base_class_specified_here) + << BaseDecl; return nullptr; } @@ -2818,21 +2806,20 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // the flexible array member would index into the subsequent base. // - If the layout determines that base comes before the derived class, // the flexible array member would index into the derived class. - if (CXXBaseDecl->hasFlexibleArrayMember()) { + if (BaseDecl->hasFlexibleArrayMember()) { Diag(BaseLoc, diag::err_base_class_has_flexible_array_member) - << CXXBaseDecl->getDeclName(); + << BaseDecl->getDeclName(); return nullptr; } // C++ [class]p3: // If a class is marked final and it appears as a base-type-specifier in // base-clause, the program is ill-formed. - if (FinalAttr *FA = CXXBaseDecl->getAttr<FinalAttr>()) { + if (FinalAttr *FA = BaseDecl->getAttr<FinalAttr>()) { Diag(BaseLoc, diag::err_class_marked_final_used_as_base) - << CXXBaseDecl->getDeclName() - << FA->isSpelledAsSealed(); - Diag(CXXBaseDecl->getLocation(), diag::note_entity_declared_at) - << CXXBaseDecl->getDeclName() << FA->getRange(); + << BaseDecl->getDeclName() << FA->isSpelledAsSealed(); + Diag(BaseDecl->getLocation(), diag::note_entity_declared_at) + << BaseDecl->getDeclName() << FA->getRange(); return nullptr; } @@ -2887,13 +2874,20 @@ BaseResult Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange, UPPC_BaseType)) return true; + // C++ [class.union.general]p4: + // [...] A union shall not have base classes. + if (Class->isUnion()) { + Diag(Class->getLocation(), diag::err_base_clause_on_union) + << SpecifierRange; + return true; + } + if (CXXBaseSpecifier *BaseSpec = CheckBaseSpecifier(Class, SpecifierRange, Virtual, Access, TInfo, EllipsisLoc)) return BaseSpec; - else - Class->setInvalidDecl(); + Class->setInvalidDecl(); return true; } diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp index be07ab0a48b33..0fa98ad101f6c 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp @@ -141,11 +141,15 @@ namespace InhCtor { // ill-formed. template<typename T> struct S : T { - struct U : S { // expected-note 6{{candidate}} - using S::S; - }; + struct U; // expected-note 6{{candidate}} using T::T; }; + + template<typename T> + struct S<T>::U : S { + using S::S; + }; + S<A>::U ua(0); // expected-error {{no match}} S<B>::U ub(0); // expected-error {{no match}} diff --git a/clang/test/SemaTemplate/dependent-names.cpp b/clang/test/SemaTemplate/dependent-names.cpp index 641ec950054f5..58d3d3eb4a6ff 100644 --- a/clang/test/SemaTemplate/dependent-names.cpp +++ b/clang/test/SemaTemplate/dependent-names.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s typedef double A; template<typename T> class B { @@ -334,8 +334,8 @@ int arr[sizeof(Sub)]; namespace PR11421 { template < unsigned > struct X { static const unsigned dimension = 3; - template<unsigned dim=dimension> - struct Y: Y<dim> { }; // expected-error{{circular inheritance between 'Y<dim>' and 'Y<dim>'}} + template<unsigned dim=dimension> + struct Y: Y<dim> { }; // expected-error{{base class has incomplete type}} }; typedef X<3> X3; X3::Y<>::iterator it; // expected-error {{no type named 'iterator' in 'PR11421::X<3>::Y<>'}} @@ -344,7 +344,8 @@ X3::Y<>::iterator it; // expected-error {{no type named 'iterator' in 'PR11421:: namespace rdar12629723 { template<class T> struct X { - struct C : public C { }; // expected-error{{circular inheritance between 'C' and 'rdar12629723::X::C'}} + struct C : public C { }; // expected-error{{base class has incomplete type}} + // expected-note@-1{{definition of 'rdar12629723::X::C' is not complete until the closing '}'}} struct B; diff --git a/clang/test/SemaTemplate/destructor-template.cpp b/clang/test/SemaTemplate/destructor-template.cpp index 8901882947623..7a3398308bbee 100644 --- a/clang/test/SemaTemplate/destructor-template.cpp +++ b/clang/test/SemaTemplate/destructor-template.cpp @@ -1,12 +1,14 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s template<typename A> class s0 { + template<typename B> class s1; +}; - template<typename B> class s1 : public s0<A> { - ~s1() {} - s0<A> ms0; - }; - +template<typename A> +template<typename B> +class s0<A>::s1 : s0<A> { + ~s1() {} + s0<A> ms0; }; struct Incomplete; @@ -28,7 +30,7 @@ namespace PR6152 { y->template Y<T>::~Y<T>(); y->~Y(); } - + template struct X<int>; } diff --git a/clang/test/SemaTemplate/typo-dependent-name.cpp b/clang/test/SemaTemplate/typo-dependent-name.cpp index fb61b03e50109..5bd924241480d 100644 --- a/clang/test/SemaTemplate/typo-dependent-name.cpp +++ b/clang/test/SemaTemplate/typo-dependent-name.cpp @@ -31,8 +31,7 @@ struct Y { static int z; template<int U> - struct Inner : Y { // expected-note {{declared here}} - }; + struct Inner; // expected-note {{declared here}} bool f(T other) { // We can determine that 'inner' does not exist at parse time, so can @@ -41,5 +40,9 @@ struct Y { } }; +template<typename T> +template<int U> +struct Y<T>::Inner : Y { }; + struct Q { constexpr operator int() { return 0; } }; void use_y(Y<Q> x) { x.f(Q()); } >From 9e07409ec8f2a3a35bc24d20529a56e558550d06 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 17 May 2024 13:52:56 -0400 Subject: [PATCH 2/7] [FOLD] refactor --- clang/lib/Sema/SemaDeclCXX.cpp | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 84033a602f131..6bd77c0fec2ae 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2710,14 +2710,14 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, return nullptr; } - if (EllipsisLoc.isValid() && !BaseType->containsUnexpandedParameterPack()) { + if (EllipsisLoc.isValid() && + !BaseType->containsUnexpandedParameterPack()) { Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) << TInfo->getTypeLoc().getSourceRange(); EllipsisLoc = SourceLocation(); } - auto *BaseDecl = - dyn_cast_if_present<CXXRecordDecl>(computeDeclContext(BaseType)); + auto *BaseDecl = dyn_cast_if_present<CXXRecordDecl>(computeDeclContext(BaseType)); // C++ [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 @@ -2734,23 +2734,18 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, if (Context.getTargetInfo().getCXXABI().isMicrosoft() || Context.getTargetInfo().getTriple().isPS()) { if (Attr *ClassAttr = getDLLAttr(Class)) { - if (auto *BaseSpec = - dyn_cast<ClassTemplateSpecializationDecl>(BaseDecl)) { - propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseSpec, - BaseLoc); + if (auto *BaseSpec = dyn_cast<ClassTemplateSpecializationDecl>(BaseDecl)) { + propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseSpec, BaseLoc); } } } - if (RequireCompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class, - SpecifierRange)) { + if (RequireCompleteType(BaseLoc, BaseType, + diag::err_incomplete_base_class, SpecifierRange)) { Class->setInvalidDecl(); return nullptr; } - } else if (!BaseType->isDependentType()) { - Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange; - return nullptr; - } else { + } else if (BaseType->isDependentType()) { // Make sure that we don't have circular inheritance among our dependent // bases. For non-dependent bases, the check for completeness below handles // this. @@ -2768,7 +2763,6 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, return nullptr; } } - // Make sure that we don't make an ill-formed AST where the type of the // Class is non-dependent and its attached base class specifier is an // dependent type, which violates invariants in many clang code paths (e.g. @@ -2780,10 +2774,11 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, return new (Context) CXXBaseSpecifier( SpecifierRange, Virtual, Class->getTagKind() == TagTypeKind::Class, Access, TInfo, EllipsisLoc); + } else { + Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange; + return nullptr; } - // If the base class is polymorphic or isn't empty, the new one is/isn't, too. - assert(BaseDecl && "Record type has no declaration"); BaseDecl = BaseDecl->getDefinition(); assert(BaseDecl && "Base type is not incomplete, but has no definition"); @@ -2796,7 +2791,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, (!BaseCSA || !DerivedCSA || BaseCSA->getName() != DerivedCSA->getName())) { Diag(Class->getLocation(), diag::err_mismatched_code_seg_base); Diag(BaseDecl->getLocation(), diag::note_base_class_specified_here) - << BaseDecl; + << BaseDecl; return nullptr; } @@ -2808,7 +2803,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // the flexible array member would index into the derived class. if (BaseDecl->hasFlexibleArrayMember()) { Diag(BaseLoc, diag::err_base_class_has_flexible_array_member) - << BaseDecl->getDeclName(); + << BaseDecl->getDeclName(); return nullptr; } @@ -2817,7 +2812,8 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // base-clause, the program is ill-formed. if (FinalAttr *FA = BaseDecl->getAttr<FinalAttr>()) { Diag(BaseLoc, diag::err_class_marked_final_used_as_base) - << BaseDecl->getDeclName() << FA->isSpelledAsSealed(); + << BaseDecl->getDeclName() + << FA->isSpelledAsSealed(); Diag(BaseDecl->getLocation(), diag::note_entity_declared_at) << BaseDecl->getDeclName() << FA->getRange(); return nullptr; >From e4f9615c01a542da4b15820e65010b9c4bf980dc Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 17 May 2024 15:09:23 -0400 Subject: [PATCH 3/7] [FOLD] provide definition of injected class name while type is being defined --- clang/lib/AST/Type.cpp | 7 +- clang/lib/Sema/SemaDeclCXX.cpp | 23 ++--- .../class.derived.general/p2.cpp | 84 +++++++++++++++++++ clang/test/SemaTemplate/dependent-names.cpp | 1 + 4 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 clang/test/CXX/class.derived/class.derived.general/p2.cpp diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 4ad764b67f81f..545725d1d7511 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2374,7 +2374,12 @@ bool Type::isIncompleteType(NamedDecl **Def) const { } case InjectedClassName: { CXXRecordDecl *Rec = cast<InjectedClassNameType>(CanonicalType)->getDecl(); - return Rec->isBeingDefined(); + if (Rec->isBeingDefined()) { + if (Def) + *Def = Rec; + return true; + } + return false; } case ConstantArray: case VariableArray: diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6bd77c0fec2ae..f98339fa5748d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2710,14 +2710,14 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, return nullptr; } - if (EllipsisLoc.isValid() && - !BaseType->containsUnexpandedParameterPack()) { + if (EllipsisLoc.isValid() && !BaseType->containsUnexpandedParameterPack()) { Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) << TInfo->getTypeLoc().getSourceRange(); EllipsisLoc = SourceLocation(); } - auto *BaseDecl = dyn_cast_if_present<CXXRecordDecl>(computeDeclContext(BaseType)); + auto *BaseDecl = + dyn_cast_if_present<CXXRecordDecl>(computeDeclContext(BaseType)); // C++ [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 @@ -2734,14 +2734,16 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, if (Context.getTargetInfo().getCXXABI().isMicrosoft() || Context.getTargetInfo().getTriple().isPS()) { if (Attr *ClassAttr = getDLLAttr(Class)) { - if (auto *BaseSpec = dyn_cast<ClassTemplateSpecializationDecl>(BaseDecl)) { - propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseSpec, BaseLoc); + if (auto *BaseSpec = + dyn_cast<ClassTemplateSpecializationDecl>(BaseDecl)) { + propagateDLLAttrToBaseClassTemplate(Class, ClassAttr, BaseSpec, + BaseLoc); } } } - if (RequireCompleteType(BaseLoc, BaseType, - diag::err_incomplete_base_class, SpecifierRange)) { + if (RequireCompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class, + SpecifierRange)) { Class->setInvalidDecl(); return nullptr; } @@ -2791,7 +2793,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, (!BaseCSA || !DerivedCSA || BaseCSA->getName() != DerivedCSA->getName())) { Diag(Class->getLocation(), diag::err_mismatched_code_seg_base); Diag(BaseDecl->getLocation(), diag::note_base_class_specified_here) - << BaseDecl; + << BaseDecl; return nullptr; } @@ -2803,7 +2805,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // the flexible array member would index into the derived class. if (BaseDecl->hasFlexibleArrayMember()) { Diag(BaseLoc, diag::err_base_class_has_flexible_array_member) - << BaseDecl->getDeclName(); + << BaseDecl->getDeclName(); return nullptr; } @@ -2812,8 +2814,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // base-clause, the program is ill-formed. if (FinalAttr *FA = BaseDecl->getAttr<FinalAttr>()) { Diag(BaseLoc, diag::err_class_marked_final_used_as_base) - << BaseDecl->getDeclName() - << FA->isSpelledAsSealed(); + << BaseDecl->getDeclName() << FA->isSpelledAsSealed(); Diag(BaseDecl->getLocation(), diag::note_entity_declared_at) << BaseDecl->getDeclName() << FA->getRange(); return nullptr; diff --git a/clang/test/CXX/class.derived/class.derived.general/p2.cpp b/clang/test/CXX/class.derived/class.derived.general/p2.cpp new file mode 100644 index 0000000000000..0930375a45737 --- /dev/null +++ b/clang/test/CXX/class.derived/class.derived.general/p2.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify + +namespace CurrentInstantiation { + template<typename T> + struct A0 { // expected-note 6{{definition of 'A0<T>' is not complete until the closing '}'}} + struct B0 : A0 { }; // expected-error {{base class has incomplete type}} + + template<typename U> + struct B1 : A0 { }; // expected-error {{base class has incomplete type}} + + struct B2; + + template<typename U> + struct B3; + + struct B4 { // expected-note 2{{definition of 'CurrentInstantiation::A0::B4' is not complete until the closing '}'}} + struct C0 : A0, B4 { }; // expected-error 2{{base class has incomplete type}} + + template<typename V> + struct C1 : A0, B4 { }; // expected-error 2{{base class has incomplete type}} + + struct C2; + + template<typename V> + struct C3; + }; + + template<typename U> + struct B5 { // expected-note 2{{definition of 'B5<U>' is not complete until the closing '}'}} + struct C0 : A0, B5 { }; // expected-error 2{{base class has incomplete type}} + + template<typename V> + struct C1 : A0, B5 { }; // expected-error 2{{base class has incomplete type}} + + struct C2; + + template<typename V> + struct C3; + }; + }; + + template<typename T> + struct A0<T>::B2 : A0 { }; + + template<typename T> + template<typename U> + struct A0<T>::B3 : A0 { }; + + template<typename T> + struct A0<T>::B4::C2 : A0, B4 { }; + + template<typename T> + template<typename V> + struct A0<T>::B4::C3 : A0, B4 { }; + + template<typename T> + template<typename U> + struct A0<T>::B5<U>::C2 : A0, B5 { }; + + template<typename T> + template<typename U> + template<typename V> + struct A0<T>::B5<U>::C3 : A0, B5 { }; + + template<typename T> + struct A0<T*> { // expected-note 2{{definition of 'A0<type-parameter-0-0 *>' is not complete until the closing '}'}} + struct B0 : A0 { }; // expected-error {{base class has incomplete type}} + + template<typename U> + struct B1 : A0 { }; // expected-error {{base class has incomplete type}} + + struct B2; + + template<typename U> + struct B3; + }; + + template<typename T> + struct A0<T*>::B2 : A0 { }; + + template<typename T> + template<typename U> + struct A0<T*>::B3 : A0 { }; +} diff --git a/clang/test/SemaTemplate/dependent-names.cpp b/clang/test/SemaTemplate/dependent-names.cpp index 58d3d3eb4a6ff..f3a7b0915b7b1 100644 --- a/clang/test/SemaTemplate/dependent-names.cpp +++ b/clang/test/SemaTemplate/dependent-names.cpp @@ -336,6 +336,7 @@ template < unsigned > struct X { static const unsigned dimension = 3; template<unsigned dim=dimension> struct Y: Y<dim> { }; // expected-error{{base class has incomplete type}} + // expected-note@-1{{definition of 'Y<dim>' is not complete until the closing '}'}} }; typedef X<3> X3; X3::Y<>::iterator it; // expected-error {{no type named 'iterator' in 'PR11421::X<3>::Y<>'}} >From 7ff4a0ba4ad3506e4ab70178398bdac838b91c34 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 17 May 2024 15:51:44 -0400 Subject: [PATCH 4/7] [Clang][Sema] Do not diagnose circular inheritence with base classes that are members of the current instantiation --- .../clang/Basic/DiagnosticSemaKinds.td | 2 - clang/lib/Sema/SemaDeclCXX.cpp | 49 ------------------- .../class.derived.general/p2.cpp | 34 ++++++++++++- clang/test/SemaTemplate/dependent-names.cpp | 4 +- 4 files changed, 35 insertions(+), 54 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e3b4186f1b06f..e3c65cba4886a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9464,8 +9464,6 @@ def err_static_data_member_not_allowed_in_local_class : Error< def err_base_clause_on_union : Error<"unions cannot have base classes">; def err_base_must_be_class : Error<"base specifier must name a class">; def err_union_as_base_class : Error<"unions cannot be base classes">; -def err_circular_inheritance : Error< - "circular inheritance between %0 and %1">; def err_base_class_has_flexible_array_member : Error< "base class %0 has a flexible array member">; def err_incomplete_base_class : Error<"base class has incomplete type">; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index f98339fa5748d..c92f5f5406fec 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2656,38 +2656,6 @@ bool Sema::isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS) { return false; } -/// Determine whether the given class is a base class of the given -/// class, including looking at dependent bases. -static bool findCircularInheritance(const CXXRecordDecl *Class, - const CXXRecordDecl *Current) { - SmallVector<const CXXRecordDecl*, 8> Queue; - - Class = Class->getCanonicalDecl(); - while (true) { - for (const auto &I : Current->bases()) { - CXXRecordDecl *Base = I.getType()->getAsCXXRecordDecl(); - if (!Base) - continue; - - Base = Base->getDefinition(); - if (!Base) - continue; - - if (Base->getCanonicalDecl() == Class) - return true; - - Queue.push_back(Base); - } - - if (Queue.empty()) - return false; - - Current = Queue.pop_back_val(); - } - - return false; -} - /// Check the validity of a C++ base class specifier. /// /// \returns a new CXXBaseSpecifier if well-formed, emits diagnostics @@ -2748,23 +2716,6 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, return nullptr; } } else if (BaseType->isDependentType()) { - // Make sure that we don't have circular inheritance among our dependent - // bases. For non-dependent bases, the check for completeness below handles - // this. - if (CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl()) { - if (BaseDecl->getCanonicalDecl() == Class->getCanonicalDecl() || - ((BaseDecl = BaseDecl->getDefinition()) && - findCircularInheritance(Class, BaseDecl))) { - Diag(BaseLoc, diag::err_circular_inheritance) - << BaseType << Context.getTypeDeclType(Class); - - if (BaseDecl->getCanonicalDecl() != Class->getCanonicalDecl()) - Diag(BaseDecl->getLocation(), diag::note_previous_decl) - << BaseType; - - return nullptr; - } - } // Make sure that we don't make an ill-formed AST where the type of the // Class is non-dependent and its attached base class specifier is an // dependent type, which violates invariants in many clang code paths (e.g. diff --git a/clang/test/CXX/class.derived/class.derived.general/p2.cpp b/clang/test/CXX/class.derived/class.derived.general/p2.cpp index 0930375a45737..888d9cd7a939d 100644 --- a/clang/test/CXX/class.derived/class.derived.general/p2.cpp +++ b/clang/test/CXX/class.derived/class.derived.general/p2.cpp @@ -81,4 +81,36 @@ namespace CurrentInstantiation { template<typename T> template<typename U> struct A0<T*>::B3 : A0 { }; -} +} // namespace CurrentInstantiation + +namespace MemberOfCurrentInstantiation { + template<typename T> + struct A0 { + struct B : B { }; // expected-error {{base class has incomplete type}} + // expected-note@-1 {{definition of 'MemberOfCurrentInstantiation::A0::B' is not complete until the closing '}'}} + + template<typename U> + struct C : C<U> { }; // expected-error {{base class has incomplete type}} + // expected-note@-1 {{definition of 'C<U>' is not complete until the closing '}'}} + }; + + template<typename T> + struct A1 { + struct B; // expected-note {{definition of 'MemberOfCurrentInstantiation::A1<long>::B' is not complete until the closing '}'}} + + struct C : B { }; // expected-error {{base class has incomplete type}} + + struct B : C { }; // expected-note {{in instantiation of member class 'MemberOfCurrentInstantiation::A1<long>::C' requested here}} + }; + + template struct A1<long>; // expected-note {{in instantiation of member class 'MemberOfCurrentInstantiation::A1<long>::B' requested here}} + + template<> + struct A1<short>::B { + static constexpr bool f() { + return true; + } + }; + + static_assert(A1<short>::C::f()); +} // namespace MemberOfCurrentInstantiation diff --git a/clang/test/SemaTemplate/dependent-names.cpp b/clang/test/SemaTemplate/dependent-names.cpp index f3a7b0915b7b1..a7260b194462c 100644 --- a/clang/test/SemaTemplate/dependent-names.cpp +++ b/clang/test/SemaTemplate/dependent-names.cpp @@ -350,7 +350,7 @@ namespace rdar12629723 { struct B; - struct A : public B { // expected-note{{'A' declared here}} + struct A : public B { virtual void foo() { } }; @@ -359,7 +359,7 @@ namespace rdar12629723 { }; template<class T> - struct X<T>::B : public A { // expected-error{{circular inheritance between 'A' and 'rdar12629723::X::B'}} + struct X<T>::B : public A { virtual void foo() { } }; } >From 5d903b3df21178d48fb214847449c9476f7ad197 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 17 May 2024 17:36:47 -0400 Subject: [PATCH 5/7] [FOLD] update clang-tidy test --- .../cppcoreguidelines/pro-type-member-init-no-crash.cpp | 8 ++++++++ .../checkers/cppcoreguidelines/pro-type-member-init.cpp | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp index 300fff6cb179b..2e2964dda1daf 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp @@ -5,3 +5,11 @@ struct X { // CHECK-MESSAGES: :[[@LINE-1]]:5: error: field has incomplete type 'X' [clang-diagnostic-error] int a = 10; }; + +template <typename T> class NoCrash { + // CHECK-MESSAGES: :[[@LINE+2]]:20: error: base class has incomplete type + // CHECK-MESSAGES: :[[@LINE-2]]:29: note: definition of 'NoCrash<T>' is not complete until the closing '}' + class B : public NoCrash { + template <typename U> B(U u) {} + }; +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp index 8d6992afef08a..eaa73b906ce09 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp @@ -463,12 +463,6 @@ struct NegativeIncompleteArrayMember { char e[]; }; -template <typename T> class NoCrash { - class B : public NoCrash { - template <typename U> B(U u) {} - }; -}; - struct PositiveBitfieldMember { PositiveBitfieldMember() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F >From ee55a6e6ad84adcb0fa5075c84ddc2f174840050 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Mon, 20 May 2024 10:59:14 -0400 Subject: [PATCH 6/7] [FOLD] rearrange condition --- clang/lib/AST/Type.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 545725d1d7511..3b90b8229dd18 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2374,12 +2374,11 @@ bool Type::isIncompleteType(NamedDecl **Def) const { } case InjectedClassName: { CXXRecordDecl *Rec = cast<InjectedClassNameType>(CanonicalType)->getDecl(); - if (Rec->isBeingDefined()) { - if (Def) - *Def = Rec; - return true; - } - return false; + if (!Rec->isBeingDefined()) + return false; + if (Def) + *Def = Rec; + return true; } case ConstantArray: case VariableArray: >From 9e23a6914bea33fa0e3aa723facc820a212f594d Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Mon, 20 May 2024 11:11:10 -0400 Subject: [PATCH 7/7] [FOLD] minor refactor of CheckBaseSpecifier --- clang/lib/Sema/SemaDeclCXX.cpp | 109 ++++++++++++++++----------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c92f5f5406fec..104e27139fe47 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2660,17 +2660,11 @@ bool Sema::isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS) { /// /// \returns a new CXXBaseSpecifier if well-formed, emits diagnostics /// and returns NULL otherwise. -CXXBaseSpecifier * -Sema::CheckBaseSpecifier(CXXRecordDecl *Class, - SourceRange SpecifierRange, - bool Virtual, AccessSpecifier Access, - TypeSourceInfo *TInfo, - SourceLocation EllipsisLoc) { - // In HLSL, unspecified class access is public rather than private. - if (getLangOpts().HLSL && Class->getTagKind() == TagTypeKind::Class && - Access == AS_none) - Access = AS_public; - +CXXBaseSpecifier *Sema::CheckBaseSpecifier(CXXRecordDecl *Class, + SourceRange SpecifierRange, + bool Virtual, AccessSpecifier Access, + TypeSourceInfo *TInfo, + SourceLocation EllipsisLoc) { QualType BaseType = TInfo->getType(); SourceLocation BaseLoc = TInfo->getTypeLoc().getBeginLoc(); if (BaseType->containsErrors()) { @@ -2715,6 +2709,50 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, Class->setInvalidDecl(); return nullptr; } + + BaseDecl = BaseDecl->getDefinition(); + assert(BaseDecl && "Base type is not incomplete, but has no definition"); + + // Microsoft docs say: + // "If a base-class has a code_seg attribute, derived classes must have the + // same attribute." + const auto *BaseCSA = BaseDecl->getAttr<CodeSegAttr>(); + const auto *DerivedCSA = Class->getAttr<CodeSegAttr>(); + if ((DerivedCSA || BaseCSA) && + (!BaseCSA || !DerivedCSA || + BaseCSA->getName() != DerivedCSA->getName())) { + Diag(Class->getLocation(), diag::err_mismatched_code_seg_base); + Diag(BaseDecl->getLocation(), diag::note_base_class_specified_here) + << BaseDecl; + return nullptr; + } + + // A class which contains a flexible array member is not suitable for use as + // a base class: + // - If the layout determines that a base comes before another base, + // the flexible array member would index into the subsequent base. + // - If the layout determines that base comes before the derived class, + // the flexible array member would index into the derived class. + if (BaseDecl->hasFlexibleArrayMember()) { + Diag(BaseLoc, diag::err_base_class_has_flexible_array_member) + << BaseDecl->getDeclName(); + return nullptr; + } + + // C++ [class]p3: + // If a class is marked final and it appears as a base-type-specifier in + // base-clause, the program is ill-formed. + if (FinalAttr *FA = BaseDecl->getAttr<FinalAttr>()) { + Diag(BaseLoc, diag::err_class_marked_final_used_as_base) + << BaseDecl->getDeclName() << FA->isSpelledAsSealed(); + Diag(BaseDecl->getLocation(), diag::note_entity_declared_at) + << BaseDecl->getDeclName() << FA->getRange(); + return nullptr; + } + + // If the base class is invalid the derived class is as well. + if (BaseDecl->isInvalidDecl()) + Class->setInvalidDecl(); } else if (BaseType->isDependentType()) { // Make sure that we don't make an ill-formed AST where the type of the // Class is non-dependent and its attached base class specifier is an @@ -2724,55 +2762,16 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // emitted. if (!Class->isDependentContext()) Class->setInvalidDecl(); - return new (Context) CXXBaseSpecifier( - SpecifierRange, Virtual, Class->getTagKind() == TagTypeKind::Class, - Access, TInfo, EllipsisLoc); } else { + // The base class is some non-dependent non-class type. Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange; return nullptr; } - BaseDecl = BaseDecl->getDefinition(); - assert(BaseDecl && "Base type is not incomplete, but has no definition"); - - // Microsoft docs say: - // "If a base-class has a code_seg attribute, derived classes must have the - // same attribute." - const auto *BaseCSA = BaseDecl->getAttr<CodeSegAttr>(); - const auto *DerivedCSA = Class->getAttr<CodeSegAttr>(); - if ((DerivedCSA || BaseCSA) && - (!BaseCSA || !DerivedCSA || BaseCSA->getName() != DerivedCSA->getName())) { - Diag(Class->getLocation(), diag::err_mismatched_code_seg_base); - Diag(BaseDecl->getLocation(), diag::note_base_class_specified_here) - << BaseDecl; - return nullptr; - } - - // A class which contains a flexible array member is not suitable for use as a - // base class: - // - If the layout determines that a base comes before another base, - // the flexible array member would index into the subsequent base. - // - If the layout determines that base comes before the derived class, - // the flexible array member would index into the derived class. - if (BaseDecl->hasFlexibleArrayMember()) { - Diag(BaseLoc, diag::err_base_class_has_flexible_array_member) - << BaseDecl->getDeclName(); - return nullptr; - } - - // C++ [class]p3: - // If a class is marked final and it appears as a base-type-specifier in - // base-clause, the program is ill-formed. - if (FinalAttr *FA = BaseDecl->getAttr<FinalAttr>()) { - Diag(BaseLoc, diag::err_class_marked_final_used_as_base) - << BaseDecl->getDeclName() << FA->isSpelledAsSealed(); - Diag(BaseDecl->getLocation(), diag::note_entity_declared_at) - << BaseDecl->getDeclName() << FA->getRange(); - return nullptr; - } - - if (BaseDecl->isInvalidDecl()) - Class->setInvalidDecl(); + // In HLSL, unspecified class access is public rather than private. + if (getLangOpts().HLSL && Class->getTagKind() == TagTypeKind::Class && + Access == AS_none) + Access = AS_public; // Create the base specifier. return new (Context) CXXBaseSpecifier( _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits