Skip to content

Commit

Permalink
[clang] Implement divergence for TypedefType and UsingType
Browse files Browse the repository at this point in the history
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 <mizvekov@gmail.com>

Differential Revision: https://reviews.llvm.org/D133468
  • Loading branch information
mizvekov committed Oct 3, 2022
1 parent eda6ff3 commit 69a6417
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 42 deletions.
1 change: 1 addition & 0 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -246,6 +246,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
TemplateSpecializationTypes;
mutable llvm::FoldingSet<ParenType> ParenTypes{GeneralTypesLog2InitSize};
mutable llvm::FoldingSet<UsingType> UsingTypes;
mutable llvm::FoldingSet<TypedefType> TypedefTypes;
mutable llvm::FoldingSet<ElaboratedType> ElaboratedTypes{
GeneralTypesLog2InitSize};
mutable llvm::FoldingSet<DependentNameType> DependentNameTypes;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/JSONNodeDumper.h
Expand Up @@ -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);
Expand Down
62 changes: 55 additions & 7 deletions clang/include/clang/AST/Type.h
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<UsingType, QualType> {
UsingShadowDecl *Found;
friend class ASTContext; // ASTContext creates these.
friend TrailingObjects;

UsingType(const UsingShadowDecl *Found, QualType Underlying, QualType Canon);

Expand All @@ -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<TypedefType, QualType> {
TypedefNameDecl *Decl;

private:
friend class ASTContext; // ASTContext creates these.
friend TrailingObjects;

TypedefType(TypeClass tc, const TypedefNameDecl *D, QualType underlying,
QualType can);
Expand All @@ -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; }
};

Expand Down
10 changes: 3 additions & 7 deletions clang/include/clang/AST/TypeProperties.td
Expand Up @@ -379,16 +379,12 @@ let Class = TypedefType in {
def : Property<"declaration", DeclRef> {
let Read = [{ node->getDecl() }];
}
def : Property<"canonicalType", Optional<QualType>> {
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<TypedefNameDecl>(declaration),
finalCanonicalType);
return ctx.getTypedefType(cast<TypedefNameDecl>(declaration), underlyingType);
}]>;
}

Expand Down
66 changes: 46 additions & 20 deletions clang/lib/AST/ASTContext.cpp
Expand Up @@ -2364,12 +2364,12 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
return getTypeInfo(cast<UsingType>(T)->desugar().getTypePtr());

case Type::Typedef: {
const TypedefNameDecl *Typedef = cast<TypedefType>(T)->getDecl();
TypeInfo Info = getTypeInfo(Typedef->getUnderlyingType().getTypePtr());
const auto *TT = cast<TypedefType>(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 {
Expand Down Expand Up @@ -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<QualType>(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<TypeDecl>(Found->getTargetDecl())));
QualType Canon = Underlying.getCanonicalType();
const Type *TypeForDecl =
cast<TypeDecl>(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<QualType>(!Underlying.isNull()),
TypeAlignment);
UsingType *NewType = new (Mem) UsingType(Found, Underlying, Canon);
Types.push_back(NewType);
UsingTypes.InsertNode(NewType, InsertPos);
return QualType(NewType, 0);
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/AST/ASTImporter.cpp
Expand Up @@ -1362,8 +1362,12 @@ ExpectedType ASTNodeImporter::VisitTypedefType(const TypedefType *T) {
Expected<TypedefNameDecl *> 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) {
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/AST/ASTStructuralEquivalence.cpp
Expand Up @@ -957,11 +957,17 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
if (!IsStructurallyEquivalent(Context, cast<UsingType>(T1)->getFoundDecl(),
cast<UsingType>(T2)->getFoundDecl()))
return false;
if (!IsStructurallyEquivalent(Context,
cast<UsingType>(T1)->getUnderlyingType(),
cast<UsingType>(T2)->getUnderlyingType()))
return false;
break;

case Type::Typedef:
if (!IsStructurallyEquivalent(Context, cast<TypedefType>(T1)->getDecl(),
cast<TypedefType>(T2)->getDecl()))
cast<TypedefType>(T2)->getDecl()) ||
!IsStructurallyEquivalent(Context, cast<TypedefType>(T1)->desugar(),
cast<TypedefType>(T2)->desugar()))
return false;
break;

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/JSONNodeDumper.cpp
Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/TextNodeDumper.cpp
Expand Up @@ -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) {
Expand Down
21 changes: 15 additions & 6 deletions clang/lib/AST/Type.cpp
Expand Up @@ -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<TypedefNameDecl *>(D)) {
assert(!isa<TypedefType>(can) && "Invalid canonical type");
TypedefBits.hasTypeDifferentFromDecl = !Underlying.isNull();
if (!typeMatchesDecl())
*getTrailingObjects<QualType>() = Underlying;
}

QualType TypedefType::desugar() const {
return getDecl()->getUnderlyingType();
return typeMatchesDecl() ? Decl->getUnderlyingType()
: *getTrailingObjects<QualType>();
}

UsingType::UsingType(const UsingShadowDecl *Found, QualType Underlying,
QualType Canon)
: Type(Using, Canon, toSemanticDependence(Underlying->getDependence())),
: Type(Using, Canon, toSemanticDependence(Canon->getDependence())),
Found(const_cast<UsingShadowDecl *>(Found)) {
assert(Underlying == getUnderlyingType());
UsingBits.hasTypeDifferentFromDecl = !Underlying.isNull();
if (!typeMatchesDecl())
*getTrailingObjects<QualType>() = Underlying;
}

QualType UsingType::getUnderlyingType() const {
return QualType(cast<TypeDecl>(Found->getTargetDecl())->getTypeForDecl(), 0);
return typeMatchesDecl()
? QualType(
cast<TypeDecl>(Found->getTargetDecl())->getTypeForDecl(), 0)
: *getTrailingObjects<QualType>();
}

QualType MacroQualifiedType::desugar() const { return getUnderlyingType(); }
Expand Down
19 changes: 19 additions & 0 deletions clang/test/SemaCXX/sugar-common-types.cpp
Expand Up @@ -112,3 +112,22 @@ C1<X2> auto t26_2 = (::SB1){};
C2<X2> 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')}}

0 comments on commit 69a6417

Please sign in to comment.