From 69a6417406a1b0316a1fa6aeb63339d0e1d2abbd Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Sun, 4 Sep 2022 04:45:40 +0200 Subject: [PATCH] [clang] Implement divergence for TypedefType and UsingType With this patch, TypedefTypes and UsingTypes can have an underlying type which diverges from their corresponding declarations. For the TypedefType case, this can be seen when getting the common sugared type between two redeclarations with different sugar. For both cases, this will become important as resugaring is implemented, as this will allow us to resugar these when they were dependent before instantiation. Signed-off-by: Matheus Izvekov Differential Revision: https://reviews.llvm.org/D133468 --- clang/include/clang/AST/ASTContext.h | 1 + clang/include/clang/AST/JSONNodeDumper.h | 1 + clang/include/clang/AST/Type.h | 62 +++++++++++++++++--- clang/include/clang/AST/TypeProperties.td | 10 +--- clang/lib/AST/ASTContext.cpp | 66 +++++++++++++++------- clang/lib/AST/ASTImporter.cpp | 6 +- clang/lib/AST/ASTStructuralEquivalence.cpp | 8 ++- clang/lib/AST/JSONNodeDumper.cpp | 8 +++ clang/lib/AST/TextNodeDumper.cpp | 4 ++ clang/lib/AST/Type.cpp | 21 +++++-- clang/test/SemaCXX/sugar-common-types.cpp | 19 +++++++ 11 files changed, 164 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 787e4234d4ea9..c4954f0d11a3d 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -246,6 +246,7 @@ class ASTContext : public RefCountedBase { TemplateSpecializationTypes; mutable llvm::FoldingSet ParenTypes{GeneralTypesLog2InitSize}; mutable llvm::FoldingSet UsingTypes; + mutable llvm::FoldingSet TypedefTypes; mutable llvm::FoldingSet ElaboratedTypes{ GeneralTypesLog2InitSize}; mutable llvm::FoldingSet DependentNameTypes; diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index c0c902da7c0d9..bff72419c8f3a 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -209,6 +209,7 @@ class JSONNodeDumper void Visit(const APValue &Value, QualType Ty); void VisitTypedefType(const TypedefType *TT); + void VisitUsingType(const UsingType *TT); void VisitFunctionType(const FunctionType *T); void VisitFunctionProtoType(const FunctionProtoType *T); void VisitRValueReferenceType(const ReferenceType *RT); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 434cbbbd10011..b7ef891f97d81 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1807,6 +1807,24 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { unsigned IsUnqual : 1; // If true: typeof_unqual, else: typeof }; + class UsingBitfields { + friend class UsingType; + + unsigned : NumTypeBits; + + /// True if the underlying type is different from the declared one. + unsigned hasTypeDifferentFromDecl : 1; + }; + + class TypedefBitfields { + friend class TypedefType; + + unsigned : NumTypeBits; + + /// True if the underlying type is different from the declared one. + unsigned hasTypeDifferentFromDecl : 1; + }; + class SubstTemplateTypeParmTypeBitfields { friend class SubstTemplateTypeParmType; @@ -1897,6 +1915,8 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { AttributedTypeBitfields AttributedTypeBits; AutoTypeBitfields AutoTypeBits; TypeOfBitfields TypeOfBits; + TypedefBitfields TypedefBits; + UsingBitfields UsingBits; BuiltinTypeBitfields BuiltinTypeBits; FunctionTypeBitfields FunctionTypeBits; ObjCObjectTypeBitfields ObjCObjectTypeBits; @@ -4476,9 +4496,12 @@ class UnresolvedUsingType : public Type { } }; -class UsingType : public Type, public llvm::FoldingSetNode { +class UsingType final : public Type, + public llvm::FoldingSetNode, + private llvm::TrailingObjects { UsingShadowDecl *Found; friend class ASTContext; // ASTContext creates these. + friend TrailingObjects; UsingType(const UsingShadowDecl *Found, QualType Underlying, QualType Canon); @@ -4487,21 +4510,31 @@ class UsingType : public Type, public llvm::FoldingSetNode { QualType getUnderlyingType() const; bool isSugared() const { return true; } + + // This always has the 'same' type as declared, but not necessarily identical. QualType desugar() const { return getUnderlyingType(); } - void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Found); } - static void Profile(llvm::FoldingSetNodeID &ID, - const UsingShadowDecl *Found) { + // Internal helper, for debugging purposes. + bool typeMatchesDecl() const { return !UsingBits.hasTypeDifferentFromDecl; } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, Found, typeMatchesDecl() ? QualType() : getUnderlyingType()); + } + static void Profile(llvm::FoldingSetNodeID &ID, const UsingShadowDecl *Found, + QualType Underlying) { ID.AddPointer(Found); + if (!Underlying.isNull()) + Underlying.Profile(ID); } static bool classof(const Type *T) { return T->getTypeClass() == Using; } }; -class TypedefType : public Type { +class TypedefType final : public Type, + public llvm::FoldingSetNode, + private llvm::TrailingObjects { TypedefNameDecl *Decl; - -private: friend class ASTContext; // ASTContext creates these. + friend TrailingObjects; TypedefType(TypeClass tc, const TypedefNameDecl *D, QualType underlying, QualType can); @@ -4510,8 +4543,23 @@ class TypedefType : public Type { TypedefNameDecl *getDecl() const { return Decl; } bool isSugared() const { return true; } + + // This always has the 'same' type as declared, but not necessarily identical. QualType desugar() const; + // Internal helper, for debugging purposes. + bool typeMatchesDecl() const { return !TypedefBits.hasTypeDifferentFromDecl; } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, Decl, typeMatchesDecl() ? QualType() : desugar()); + } + static void Profile(llvm::FoldingSetNodeID &ID, const TypedefNameDecl *Decl, + QualType Underlying) { + ID.AddPointer(Decl); + if (!Underlying.isNull()) + Underlying.Profile(ID); + } + static bool classof(const Type *T) { return T->getTypeClass() == Typedef; } }; diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 75b12b8f7cdc8..9634d2b961b18 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -379,16 +379,12 @@ let Class = TypedefType in { def : Property<"declaration", DeclRef> { let Read = [{ node->getDecl() }]; } - def : Property<"canonicalType", Optional> { - let Read = [{ makeOptionalFromNullable(node->getCanonicalTypeInternal()) }]; + def : Property<"underlyingType", QualType> { + let Read = [{ node->desugar() }]; } def : Creator<[{ - QualType finalCanonicalType = - canonicalType ? ctx.getCanonicalType(*canonicalType) - : QualType(); - return ctx.getTypedefType(cast(declaration), - finalCanonicalType); + return ctx.getTypedefType(cast(declaration), underlyingType); }]>; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 7e9b2febd7d6c..fa7d5269e3f9e 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2364,12 +2364,12 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { return getTypeInfo(cast(T)->desugar().getTypePtr()); case Type::Typedef: { - const TypedefNameDecl *Typedef = cast(T)->getDecl(); - TypeInfo Info = getTypeInfo(Typedef->getUnderlyingType().getTypePtr()); + const auto *TT = cast(T); + TypeInfo Info = getTypeInfo(TT->desugar().getTypePtr()); // If the typedef has an aligned attribute on it, it overrides any computed // alignment we have. This violates the GCC documentation (which says that // attribute(aligned) can only round up) but matches its implementation. - if (unsigned AttrAlign = Typedef->getMaxAlignment()) { + if (unsigned AttrAlign = TT->getDecl()->getMaxAlignment()) { Align = AttrAlign; AlignRequirement = AlignRequirementKind::RequiredByTypedef; } else { @@ -4638,34 +4638,60 @@ QualType ASTContext::getTypeDeclTypeSlow(const TypeDecl *Decl) const { /// specified typedef name decl. QualType ASTContext::getTypedefType(const TypedefNameDecl *Decl, QualType Underlying) const { - if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0); + if (!Decl->TypeForDecl) { + if (Underlying.isNull()) + Underlying = Decl->getUnderlyingType(); + auto *NewType = new (*this, TypeAlignment) TypedefType( + Type::Typedef, Decl, QualType(), getCanonicalType(Underlying)); + Decl->TypeForDecl = NewType; + Types.push_back(NewType); + return QualType(NewType, 0); + } + if (Underlying.isNull() || Decl->getUnderlyingType() == Underlying) + return QualType(Decl->TypeForDecl, 0); + assert(hasSameType(Decl->getUnderlyingType(), Underlying)); - if (Underlying.isNull()) - Underlying = Decl->getUnderlyingType(); - QualType Canonical = getCanonicalType(Underlying); - auto *newType = new (*this, TypeAlignment) - TypedefType(Type::Typedef, Decl, Underlying, Canonical); - Decl->TypeForDecl = newType; - Types.push_back(newType); - return QualType(newType, 0); + llvm::FoldingSetNodeID ID; + TypedefType::Profile(ID, Decl, Underlying); + + void *InsertPos = nullptr; + if (TypedefType *T = TypedefTypes.FindNodeOrInsertPos(ID, InsertPos)) { + assert(!T->typeMatchesDecl() && + "non-divergent case should be handled with TypeDecl"); + return QualType(T, 0); + } + + void *Mem = + Allocate(TypedefType::totalSizeToAlloc(true), TypeAlignment); + auto *NewType = new (Mem) TypedefType(Type::Typedef, Decl, Underlying, + getCanonicalType(Underlying)); + TypedefTypes.InsertNode(NewType, InsertPos); + Types.push_back(NewType); + return QualType(NewType, 0); } QualType ASTContext::getUsingType(const UsingShadowDecl *Found, QualType Underlying) const { llvm::FoldingSetNodeID ID; - UsingType::Profile(ID, Found); + UsingType::Profile(ID, Found, Underlying); void *InsertPos = nullptr; - UsingType *T = UsingTypes.FindNodeOrInsertPos(ID, InsertPos); - if (T) + if (UsingType *T = UsingTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(T, 0); - assert(!Underlying.hasLocalQualifiers()); - assert(Underlying == getTypeDeclType(cast(Found->getTargetDecl()))); - QualType Canon = Underlying.getCanonicalType(); + const Type *TypeForDecl = + cast(Found->getTargetDecl())->getTypeForDecl(); - UsingType *NewType = - new (*this, TypeAlignment) UsingType(Found, Underlying, Canon); + assert(!Underlying.hasLocalQualifiers()); + QualType Canon = Underlying->getCanonicalTypeInternal(); + assert(TypeForDecl->getCanonicalTypeInternal() == Canon); + + if (Underlying.getTypePtr() == TypeForDecl) + Underlying = QualType(); + void *Mem = + Allocate(UsingType::totalSizeToAlloc(!Underlying.isNull()), + TypeAlignment); + UsingType *NewType = new (Mem) UsingType(Found, Underlying, Canon); Types.push_back(NewType); UsingTypes.InsertNode(NewType, InsertPos); return QualType(NewType, 0); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 7ab425de270ab..63b258c284c71 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1362,8 +1362,12 @@ ExpectedType ASTNodeImporter::VisitTypedefType(const TypedefType *T) { Expected ToDeclOrErr = import(T->getDecl()); if (!ToDeclOrErr) return ToDeclOrErr.takeError(); + ExpectedType ToUnderlyingTypeOrErr = import(T->desugar()); + if (!ToUnderlyingTypeOrErr) + return ToUnderlyingTypeOrErr.takeError(); - return Importer.getToContext().getTypeDeclType(*ToDeclOrErr); + return Importer.getToContext().getTypedefType(*ToDeclOrErr, + *ToUnderlyingTypeOrErr); } ExpectedType ASTNodeImporter::VisitTypeOfExprType(const TypeOfExprType *T) { diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 8f2ecee801c6e..af75d38951a1f 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -957,11 +957,17 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, if (!IsStructurallyEquivalent(Context, cast(T1)->getFoundDecl(), cast(T2)->getFoundDecl())) return false; + if (!IsStructurallyEquivalent(Context, + cast(T1)->getUnderlyingType(), + cast(T2)->getUnderlyingType())) + return false; break; case Type::Typedef: if (!IsStructurallyEquivalent(Context, cast(T1)->getDecl(), - cast(T2)->getDecl())) + cast(T2)->getDecl()) || + !IsStructurallyEquivalent(Context, cast(T1)->desugar(), + cast(T2)->desugar())) return false; break; diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 14828e2f5670c..0b6ad375306ec 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -530,6 +530,14 @@ JSONNodeDumper::createCXXBaseSpecifier(const CXXBaseSpecifier &BS) { void JSONNodeDumper::VisitTypedefType(const TypedefType *TT) { JOS.attribute("decl", createBareDeclRef(TT->getDecl())); + if (!TT->typeMatchesDecl()) + JOS.attribute("type", createQualType(TT->desugar())); +} + +void JSONNodeDumper::VisitUsingType(const UsingType *TT) { + JOS.attribute("decl", createBareDeclRef(TT->getFoundDecl())); + if (!TT->typeMatchesDecl()) + JOS.attribute("type", createQualType(TT->desugar())); } void JSONNodeDumper::VisitFunctionType(const FunctionType *T) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 9bc643426c9e8..890f877ca4e19 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1543,10 +1543,14 @@ void TextNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *T) { void TextNodeDumper::VisitUsingType(const UsingType *T) { dumpDeclRef(T->getFoundDecl()); + if (!T->typeMatchesDecl()) + OS << " divergent"; } void TextNodeDumper::VisitTypedefType(const TypedefType *T) { dumpDeclRef(T->getDecl()); + if (!T->typeMatchesDecl()) + OS << " divergent"; } void TextNodeDumper::VisitUnaryTransformType(const UnaryTransformType *T) { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 787d88b02fe0d..ccad2f37e6b87 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3434,25 +3434,34 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, } TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D, - QualType underlying, QualType can) - : Type(tc, can, toSemanticDependence(underlying->getDependence())), + QualType Underlying, QualType can) + : Type(tc, can, toSemanticDependence(can->getDependence())), Decl(const_cast(D)) { assert(!isa(can) && "Invalid canonical type"); + TypedefBits.hasTypeDifferentFromDecl = !Underlying.isNull(); + if (!typeMatchesDecl()) + *getTrailingObjects() = Underlying; } QualType TypedefType::desugar() const { - return getDecl()->getUnderlyingType(); + return typeMatchesDecl() ? Decl->getUnderlyingType() + : *getTrailingObjects(); } UsingType::UsingType(const UsingShadowDecl *Found, QualType Underlying, QualType Canon) - : Type(Using, Canon, toSemanticDependence(Underlying->getDependence())), + : Type(Using, Canon, toSemanticDependence(Canon->getDependence())), Found(const_cast(Found)) { - assert(Underlying == getUnderlyingType()); + UsingBits.hasTypeDifferentFromDecl = !Underlying.isNull(); + if (!typeMatchesDecl()) + *getTrailingObjects() = Underlying; } QualType UsingType::getUnderlyingType() const { - return QualType(cast(Found->getTargetDecl())->getTypeForDecl(), 0); + return typeMatchesDecl() + ? QualType( + cast(Found->getTargetDecl())->getTypeForDecl(), 0) + : *getTrailingObjects(); } QualType MacroQualifiedType::desugar() const { return getUnderlyingType(); } diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp index 7ae3a873a3167..1932ccd9c7250 100644 --- a/clang/test/SemaCXX/sugar-common-types.cpp +++ b/clang/test/SemaCXX/sugar-common-types.cpp @@ -112,3 +112,22 @@ C1 auto t26_2 = (::SB1){}; C2 auto t26_3 = (::SB1){}; N t26 = 0 ? t26_1 : t26_2; // expected-error {{from 'SB1' (aka 'SS1')}} N t27 = 0 ? t26_1 : t26_3; // expected-error {{from 'SB1' (aka 'SS1')}} + +using RPB1 = X1*; +using RPX1 = RPB1; +using RPB1 = Y1*; // redeclared +using RPY1 = RPB1; +N t28 = *(RPB1){}; // expected-error {{lvalue of type 'Y1' (aka 'int')}} +auto t29 = 0 ? (RPX1){} : (RPY1){}; +N t30 = t29; // expected-error {{lvalue of type 'RPB1' (aka 'int *')}} +N t31 = *t29; // expected-error {{lvalue of type 'B1' (aka 'int')}} + +namespace A { using type1 = X1*; }; +namespace C { using A::type1; }; +using UPX1 = C::type1; +namespace A { using type1 = Y1*; }; // redeclared +namespace C { using A::type1; }; // redeclared +using UPY1 = C::type1; +auto t32 = 0 ? (UPX1){} : (UPY1){}; +N t33 = t32; // expected-error {{lvalue of type 'C::type1' (aka 'int *')}} +N t34 = *t32; // expected-error {{lvalue of type 'B1' (aka 'int')}}