mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 13:26:45 +00:00
[clang] ASTContext: flesh out implementation of getCommonNNS (#131964)
This properly implements getCommonNNS, for getting the common NestedNameSpecifier, for which the previous implementation was a bare minimum placeholder.
This commit is contained in:
parent
77ac5a2d57
commit
be9b7a14f4
@ -271,6 +271,8 @@ Improvements to Clang's diagnostics
|
||||
as function arguments or return value respectively. Note that
|
||||
:doc:`ThreadSafetyAnalysis` still does not perform alias analysis. The
|
||||
feature will be default-enabled with ``-Wthread-safety`` in a future release.
|
||||
- Clang will now do a better job producing common nested names, when producing
|
||||
common types for ternary operator, template argument deduction and multiple return auto deduction.
|
||||
- The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) and minus(-) as signed integers
|
||||
except for the case where the operand is an unsigned integer
|
||||
and throws warning if they are compared with unsigned integers (##18878).
|
||||
|
@ -13462,13 +13462,155 @@ static ElaboratedTypeKeyword getCommonTypeKeyword(const T *X, const T *Y) {
|
||||
: ElaboratedTypeKeyword::None;
|
||||
}
|
||||
|
||||
/// Returns a NestedNameSpecifier which has only the common sugar
|
||||
/// present in both NNS1 and NNS2.
|
||||
static NestedNameSpecifier *getCommonNNS(ASTContext &Ctx,
|
||||
NestedNameSpecifier *NNS1,
|
||||
NestedNameSpecifier *NNS2,
|
||||
bool IsSame) {
|
||||
// If they are identical, all sugar is common.
|
||||
if (NNS1 == NNS2)
|
||||
return NNS1;
|
||||
|
||||
// IsSame implies both NNSes are equivalent.
|
||||
NestedNameSpecifier *Canon = Ctx.getCanonicalNestedNameSpecifier(NNS1);
|
||||
if (Canon != Ctx.getCanonicalNestedNameSpecifier(NNS2)) {
|
||||
assert(!IsSame && "Should be the same NestedNameSpecifier");
|
||||
// If they are not the same, there is nothing to unify.
|
||||
// FIXME: It would be useful here if we could represent a canonically
|
||||
// empty NNS, which is not identical to an empty-as-written NNS.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NestedNameSpecifier *R = nullptr;
|
||||
NestedNameSpecifier::SpecifierKind K1 = NNS1->getKind(), K2 = NNS2->getKind();
|
||||
switch (K1) {
|
||||
case NestedNameSpecifier::SpecifierKind::Identifier: {
|
||||
assert(K2 == NestedNameSpecifier::SpecifierKind::Identifier);
|
||||
IdentifierInfo *II = NNS1->getAsIdentifier();
|
||||
assert(II == NNS2->getAsIdentifier());
|
||||
// For an identifier, the prefixes are significant, so they must be the
|
||||
// same.
|
||||
NestedNameSpecifier *P = ::getCommonNNS(Ctx, NNS1->getPrefix(),
|
||||
NNS2->getPrefix(), /*IsSame=*/true);
|
||||
R = NestedNameSpecifier::Create(Ctx, P, II);
|
||||
break;
|
||||
}
|
||||
case NestedNameSpecifier::SpecifierKind::Namespace:
|
||||
case NestedNameSpecifier::SpecifierKind::NamespaceAlias: {
|
||||
assert(K2 == NestedNameSpecifier::SpecifierKind::Namespace ||
|
||||
K2 == NestedNameSpecifier::SpecifierKind::NamespaceAlias);
|
||||
// The prefixes for namespaces are not significant, its declaration
|
||||
// identifies it uniquely.
|
||||
NestedNameSpecifier *P =
|
||||
::getCommonNNS(Ctx, NNS1->getPrefix(), NNS2->getPrefix(),
|
||||
/*IsSame=*/false);
|
||||
NamespaceAliasDecl *A1 = NNS1->getAsNamespaceAlias(),
|
||||
*A2 = NNS2->getAsNamespaceAlias();
|
||||
// Are they the same namespace alias?
|
||||
if (declaresSameEntity(A1, A2)) {
|
||||
R = NestedNameSpecifier::Create(Ctx, P, ::getCommonDeclChecked(A1, A2));
|
||||
break;
|
||||
}
|
||||
// Otherwise, look at the namespaces only.
|
||||
NamespaceDecl *N1 = A1 ? A1->getNamespace() : NNS1->getAsNamespace(),
|
||||
*N2 = A2 ? A2->getNamespace() : NNS2->getAsNamespace();
|
||||
R = NestedNameSpecifier::Create(Ctx, P, ::getCommonDeclChecked(N1, N2));
|
||||
break;
|
||||
}
|
||||
case NestedNameSpecifier::SpecifierKind::TypeSpec:
|
||||
case NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate: {
|
||||
// FIXME: See comment below, on Super case.
|
||||
if (K2 == NestedNameSpecifier::SpecifierKind::Super)
|
||||
return Ctx.getCanonicalNestedNameSpecifier(NNS1);
|
||||
|
||||
assert(K2 == NestedNameSpecifier::SpecifierKind::TypeSpec ||
|
||||
K2 == NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate);
|
||||
|
||||
// Only keep the template keyword if both sides have it.
|
||||
bool Template =
|
||||
K1 == NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate &&
|
||||
K2 == NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate;
|
||||
|
||||
const Type *T1 = NNS1->getAsType(), *T2 = NNS2->getAsType();
|
||||
if (T1 == T2) {
|
||||
// If the types are indentical, then only the prefixes differ.
|
||||
// A well-formed NNS never has these types, as they have
|
||||
// special normalized forms.
|
||||
assert((!isa<DependentNameType, ElaboratedType>(T1)));
|
||||
// Only for a DependentTemplateSpecializationType the prefix
|
||||
// is actually significant. A DependentName, which would be another
|
||||
// plausible case, cannot occur here, as explained above.
|
||||
bool IsSame = isa<DependentTemplateSpecializationType>(T1);
|
||||
NestedNameSpecifier *P =
|
||||
::getCommonNNS(Ctx, NNS1->getPrefix(), NNS2->getPrefix(), IsSame);
|
||||
R = NestedNameSpecifier::Create(Ctx, P, Template, T1);
|
||||
break;
|
||||
}
|
||||
// TODO: Try to salvage the original prefix.
|
||||
// If getCommonSugaredType removed any top level sugar, the original prefix
|
||||
// is not applicable anymore.
|
||||
NestedNameSpecifier *P = nullptr;
|
||||
const Type *T = Ctx.getCommonSugaredType(QualType(T1, 0), QualType(T2, 0),
|
||||
/*Unqualified=*/true)
|
||||
.getTypePtr();
|
||||
|
||||
// A NestedNameSpecifier has special normalization rules for certain types.
|
||||
switch (T->getTypeClass()) {
|
||||
case Type::Elaborated: {
|
||||
// An ElaboratedType is stripped off, it's Qualifier becomes the prefix.
|
||||
auto *ET = cast<ElaboratedType>(T);
|
||||
R = NestedNameSpecifier::Create(Ctx, ET->getQualifier(), Template,
|
||||
ET->getNamedType().getTypePtr());
|
||||
break;
|
||||
}
|
||||
case Type::DependentName: {
|
||||
// A DependentName is turned into an Identifier NNS.
|
||||
auto *DN = cast<DependentNameType>(T);
|
||||
R = NestedNameSpecifier::Create(Ctx, DN->getQualifier(),
|
||||
DN->getIdentifier());
|
||||
break;
|
||||
}
|
||||
case Type::DependentTemplateSpecialization: {
|
||||
// A DependentTemplateSpecializationType loses it's Qualifier, which
|
||||
// is turned into the prefix.
|
||||
auto *DTST = cast<DependentTemplateSpecializationType>(T);
|
||||
T = Ctx.getDependentTemplateSpecializationType(
|
||||
DTST->getKeyword(), /*NNS=*/nullptr, DTST->getIdentifier(),
|
||||
DTST->template_arguments())
|
||||
.getTypePtr();
|
||||
P = DTST->getQualifier();
|
||||
R = NestedNameSpecifier::Create(Ctx, DTST->getQualifier(), Template, T);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
R = NestedNameSpecifier::Create(Ctx, P, Template, T);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NestedNameSpecifier::SpecifierKind::Super:
|
||||
// FIXME: Can __super even be used with data members?
|
||||
// If it's only usable in functions, we will never see it here,
|
||||
// unless we save the qualifiers used in function types.
|
||||
// In that case, it might be possible NNS2 is a type,
|
||||
// in which case we should degrade the result to
|
||||
// a CXXRecordType.
|
||||
return Ctx.getCanonicalNestedNameSpecifier(NNS1);
|
||||
case NestedNameSpecifier::SpecifierKind::Global:
|
||||
// The global NNS is a singleton.
|
||||
assert(K2 == NestedNameSpecifier::SpecifierKind::Global &&
|
||||
"Global NNS cannot be equivalent to any other kind");
|
||||
llvm_unreachable("Global NestedNameSpecifiers did not compare equal");
|
||||
}
|
||||
assert(Ctx.getCanonicalNestedNameSpecifier(R) == Canon);
|
||||
return R;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static NestedNameSpecifier *getCommonNNS(ASTContext &Ctx, const T *X,
|
||||
const T *Y) {
|
||||
// FIXME: Try to keep the common NNS sugar.
|
||||
return X->getQualifier() == Y->getQualifier()
|
||||
? X->getQualifier()
|
||||
: Ctx.getCanonicalNestedNameSpecifier(X->getQualifier());
|
||||
static NestedNameSpecifier *getCommonQualifier(ASTContext &Ctx, const T *X,
|
||||
const T *Y, bool IsSame) {
|
||||
return ::getCommonNNS(Ctx, X->getQualifier(), Y->getQualifier(), IsSame);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@ -13879,8 +14021,9 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
|
||||
*NY = cast<DependentNameType>(Y);
|
||||
assert(NX->getIdentifier() == NY->getIdentifier());
|
||||
return Ctx.getDependentNameType(
|
||||
getCommonTypeKeyword(NX, NY), getCommonNNS(Ctx, NX, NY),
|
||||
NX->getIdentifier(), NX->getCanonicalTypeInternal());
|
||||
getCommonTypeKeyword(NX, NY),
|
||||
getCommonQualifier(Ctx, NX, NY, /*IsSame=*/true), NX->getIdentifier(),
|
||||
NX->getCanonicalTypeInternal());
|
||||
}
|
||||
case Type::DependentTemplateSpecialization: {
|
||||
const auto *TX = cast<DependentTemplateSpecializationType>(X),
|
||||
@ -13889,8 +14032,9 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
|
||||
auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(),
|
||||
TY->template_arguments());
|
||||
return Ctx.getDependentTemplateSpecializationType(
|
||||
getCommonTypeKeyword(TX, TY), getCommonNNS(Ctx, TX, TY),
|
||||
TX->getIdentifier(), As);
|
||||
getCommonTypeKeyword(TX, TY),
|
||||
getCommonQualifier(Ctx, TX, TY, /*IsSame=*/true), TX->getIdentifier(),
|
||||
As);
|
||||
}
|
||||
case Type::UnaryTransform: {
|
||||
const auto *TX = cast<UnaryTransformType>(X),
|
||||
@ -14047,7 +14191,8 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
|
||||
case Type::Elaborated: {
|
||||
const auto *EX = cast<ElaboratedType>(X), *EY = cast<ElaboratedType>(Y);
|
||||
return Ctx.getElaboratedType(
|
||||
::getCommonTypeKeyword(EX, EY), ::getCommonNNS(Ctx, EX, EY),
|
||||
::getCommonTypeKeyword(EX, EY),
|
||||
::getCommonQualifier(Ctx, EX, EY, /*IsSame=*/false),
|
||||
Ctx.getQualifiedType(Underlying),
|
||||
::getCommonDecl(EX->getOwnedTagDecl(), EY->getOwnedTagDecl()));
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ template <class T> struct S1 {
|
||||
};
|
||||
|
||||
N t10 = 0 ? S1<X1>() : S1<Y1>(); // expected-error {{from 'S1<B1>' (aka 'S1<int>')}}
|
||||
N t11 = 0 ? S1<X1>::S2<X2>() : S1<Y1>::S2<Y2>(); // expected-error {{from 'S1<int>::S2<B2>' (aka 'S2<void>')}}
|
||||
N t11 = 0 ? S1<X1>::S2<X2>() : S1<Y1>::S2<Y2>(); // expected-error {{from 'S1<B1>::S2<B2>' (aka 'S2<void>')}}
|
||||
|
||||
template <class T> using Al = S1<T>;
|
||||
|
||||
@ -88,7 +88,7 @@ N t19 = 0 ? (__underlying_type(EnumsX::X)){} : (__underlying_type(EnumsY::Y)){};
|
||||
// expected-error@-1 {{rvalue of type 'B1' (aka 'int')}}
|
||||
|
||||
N t20 = 0 ? (__underlying_type(EnumsX::X)){} : (__underlying_type(EnumsY::X)){};
|
||||
// expected-error@-1 {{rvalue of type '__underlying_type(Enums::X)' (aka 'int')}}
|
||||
// expected-error@-1 {{rvalue of type '__underlying_type(EnumsB::X)' (aka 'int')}}
|
||||
|
||||
using QX = const SB1 *;
|
||||
using QY = const ::SB1 *;
|
||||
|
Loading…
x
Reference in New Issue
Block a user