diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -246,6 +246,7 @@ 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 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -209,6 +209,7 @@ 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 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1807,6 +1807,24 @@ 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 @@ AttributedTypeBitfields AttributedTypeBits; AutoTypeBitfields AutoTypeBits; TypeOfBitfields TypeOfBits; + TypedefBitfields TypedefBits; + UsingBitfields UsingBits; BuiltinTypeBitfields BuiltinTypeBits; FunctionTypeBitfields FunctionTypeBits; ObjCObjectTypeBitfields ObjCObjectTypeBits; @@ -4476,9 +4496,12 @@ } }; -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 @@ 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 @@ 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 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -379,16 +379,12 @@ 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 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2364,12 +2364,12 @@ 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 @@ /// 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 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1362,8 +1362,12 @@ 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 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -957,11 +957,17 @@ 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 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -530,6 +530,14 @@ 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 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1543,10 +1543,14 @@ 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 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3434,25 +3434,34 @@ } 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 --- a/clang/test/SemaCXX/sugar-common-types.cpp +++ b/clang/test/SemaCXX/sugar-common-types.cpp @@ -112,3 +112,22 @@ 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')}}