Skip to content

Commit 68bcd9f

Browse files
committed
[clang] ASTContext: flesh out implementation of getCommonNNS
This properly implements getCommonNNS, for getting the common NestedNameSpecifier, for which the previous implementation was a bare minimum placeholder.
1 parent b326cb6 commit 68bcd9f

File tree

3 files changed

+161
-14
lines changed

3 files changed

+161
-14
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,9 @@ Improvements to Clang's diagnostics
264264
as function arguments or return value respectively. Note that
265265
:doc:`ThreadSafetyAnalysis` still does not perform alias analysis. The
266266
feature will be default-enabled with ``-Wthread-safety`` in a future release.
267-
- The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) and minus(-) as signed integers
267+
- Clang will now do a better job producing common nested names, when producing
268+
common types for ternary operator, template argument deduction and multiple return auto deduction.
269+
- The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) and minus(-) as signed integers
268270
except for the case where the operand is an unsigned integer
269271
and throws warning if they are compared with unsigned integers (##18878).
270272
- The ``-Wunnecessary-virtual-specifier`` warning has been added to warn about

clang/lib/AST/ASTContext.cpp

Lines changed: 156 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13462,13 +13462,155 @@ static ElaboratedTypeKeyword getCommonTypeKeyword(const T *X, const T *Y) {
1346213462
: ElaboratedTypeKeyword::None;
1346313463
}
1346413464

13465+
/// Returns a NestedNameSpecifier which has only the common sugar
13466+
/// present in both NNS1 and NNS2.
13467+
static NestedNameSpecifier *getCommonNNS(ASTContext &Ctx,
13468+
NestedNameSpecifier *NNS1,
13469+
NestedNameSpecifier *NNS2,
13470+
bool IsSame) {
13471+
// If they are identical, all sugar is common.
13472+
if (NNS1 == NNS2)
13473+
return NNS1;
13474+
13475+
// IsSame implies both NNSes are equivalent.
13476+
NestedNameSpecifier *Canon = Ctx.getCanonicalNestedNameSpecifier(NNS1);
13477+
if (Canon != Ctx.getCanonicalNestedNameSpecifier(NNS2)) {
13478+
assert(!IsSame && "Should be the same NestedNameSpecifier");
13479+
// If they are not the same, there is nothing to unify.
13480+
// FIXME: It would be useful here if we could represent a canonically
13481+
// empty NNS, which is not identical to an empty-as-written NNS.
13482+
return nullptr;
13483+
}
13484+
13485+
NestedNameSpecifier *R = nullptr;
13486+
NestedNameSpecifier::SpecifierKind K1 = NNS1->getKind(), K2 = NNS2->getKind();
13487+
switch (K1) {
13488+
case NestedNameSpecifier::SpecifierKind::Identifier: {
13489+
assert(K2 == NestedNameSpecifier::SpecifierKind::Identifier);
13490+
IdentifierInfo *II = NNS1->getAsIdentifier();
13491+
assert(II == NNS2->getAsIdentifier());
13492+
// For an identifier, the prefixes are significant, so they must be the
13493+
// same.
13494+
NestedNameSpecifier *P = ::getCommonNNS(Ctx, NNS1->getPrefix(),
13495+
NNS2->getPrefix(), /*IsSame=*/true);
13496+
R = NestedNameSpecifier::Create(Ctx, P, II);
13497+
break;
13498+
}
13499+
case NestedNameSpecifier::SpecifierKind::Namespace:
13500+
case NestedNameSpecifier::SpecifierKind::NamespaceAlias: {
13501+
assert(K2 == NestedNameSpecifier::SpecifierKind::Namespace ||
13502+
K2 == NestedNameSpecifier::SpecifierKind::NamespaceAlias);
13503+
// The prefixes for namespaces are not significant, its declaration
13504+
// identifies it uniquely.
13505+
NestedNameSpecifier *P =
13506+
::getCommonNNS(Ctx, NNS1->getPrefix(), NNS2->getPrefix(),
13507+
/*IsSame=*/false);
13508+
NamespaceAliasDecl *A1 = NNS1->getAsNamespaceAlias(),
13509+
*A2 = NNS2->getAsNamespaceAlias();
13510+
// Are they the same namespace alias?
13511+
if (declaresSameEntity(A1, A2)) {
13512+
R = NestedNameSpecifier::Create(Ctx, P, ::getCommonDeclChecked(A1, A2));
13513+
break;
13514+
}
13515+
// Otherwise, look at the namespaces only.
13516+
NamespaceDecl *N1 = A1 ? A1->getNamespace() : NNS1->getAsNamespace(),
13517+
*N2 = A2 ? A2->getNamespace() : NNS2->getAsNamespace();
13518+
R = NestedNameSpecifier::Create(Ctx, P, ::getCommonDeclChecked(N1, N2));
13519+
break;
13520+
}
13521+
case NestedNameSpecifier::SpecifierKind::TypeSpec:
13522+
case NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate: {
13523+
// FIXME: See comment below, on Super case.
13524+
if (K2 == NestedNameSpecifier::SpecifierKind::Super)
13525+
return Ctx.getCanonicalNestedNameSpecifier(NNS1);
13526+
13527+
assert(K2 == NestedNameSpecifier::SpecifierKind::TypeSpec ||
13528+
K2 == NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate);
13529+
13530+
// Only keep the template keyword if both sides have it.
13531+
bool Template =
13532+
K1 == NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate &&
13533+
K2 == NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate;
13534+
13535+
const Type *T1 = NNS1->getAsType(), *T2 = NNS2->getAsType();
13536+
if (T1 == T2) {
13537+
// If the types are indentical, then only the prefixes differ.
13538+
// A well-formed NNS never has these types, as they have
13539+
// special normalized forms.
13540+
assert((!isa<DependentNameType, ElaboratedType>(T1)));
13541+
// Only for a DependentTemplateSpecializationType the prefix
13542+
// is actually significant. A DependentName, which would be another
13543+
// plausible case, cannot occur here, as explained above.
13544+
bool IsSame = isa<DependentTemplateSpecializationType>(T1);
13545+
NestedNameSpecifier *P =
13546+
::getCommonNNS(Ctx, NNS1->getPrefix(), NNS2->getPrefix(), IsSame);
13547+
R = NestedNameSpecifier::Create(Ctx, P, Template, T1);
13548+
break;
13549+
}
13550+
// TODO: Try to salvage the original prefix.
13551+
// If getCommonSugaredType removed any top level sugar, the original prefix
13552+
// is not applicable anymore.
13553+
NestedNameSpecifier *P = nullptr;
13554+
const Type *T = Ctx.getCommonSugaredType(QualType(T1, 0), QualType(T2, 0),
13555+
/*Unqualified=*/true)
13556+
.getTypePtr();
13557+
13558+
// A NestedNameSpecifier has special normalization rules for certain types.
13559+
switch (T->getTypeClass()) {
13560+
case Type::Elaborated: {
13561+
// An ElaboratedType is stripped off, it's Qualifier becomes the prefix.
13562+
auto *ET = cast<ElaboratedType>(T);
13563+
R = NestedNameSpecifier::Create(Ctx, ET->getQualifier(), Template,
13564+
ET->getNamedType().getTypePtr());
13565+
break;
13566+
}
13567+
case Type::DependentName: {
13568+
// A DependentName is turned into an Identifier NNS.
13569+
auto *DN = cast<DependentNameType>(T);
13570+
R = NestedNameSpecifier::Create(Ctx, DN->getQualifier(),
13571+
DN->getIdentifier());
13572+
break;
13573+
}
13574+
case Type::DependentTemplateSpecialization: {
13575+
// A DependentTemplateSpecializationType loses it's Qualifier, which
13576+
// is turned into the prefix.
13577+
auto *DTST = cast<DependentTemplateSpecializationType>(T);
13578+
T = Ctx.getDependentTemplateSpecializationType(
13579+
DTST->getKeyword(), /*NNS=*/nullptr, DTST->getIdentifier(),
13580+
DTST->template_arguments())
13581+
.getTypePtr();
13582+
P = DTST->getQualifier();
13583+
R = NestedNameSpecifier::Create(Ctx, DTST->getQualifier(), Template, T);
13584+
break;
13585+
}
13586+
default:
13587+
R = NestedNameSpecifier::Create(Ctx, P, Template, T);
13588+
break;
13589+
}
13590+
break;
13591+
}
13592+
case NestedNameSpecifier::SpecifierKind::Super:
13593+
// FIXME: Can __super even be used with data members?
13594+
// If it's only usable in functions, we will never see it here,
13595+
// unless we save the qualifiers used in function types.
13596+
// In that case, it might be possible NNS2 is a type,
13597+
// in which case we should degrade the result to
13598+
// a CXXRecordType.
13599+
return Ctx.getCanonicalNestedNameSpecifier(NNS1);
13600+
case NestedNameSpecifier::SpecifierKind::Global:
13601+
// The global NNS is a singleton.
13602+
assert(K2 == NestedNameSpecifier::SpecifierKind::Global &&
13603+
"Global NNS cannot be equivalent to any other kind");
13604+
llvm_unreachable("Global NestedNameSpecifiers did not compare equal");
13605+
}
13606+
assert(Ctx.getCanonicalNestedNameSpecifier(R) == Canon);
13607+
return R;
13608+
}
13609+
1346513610
template <class T>
13466-
static NestedNameSpecifier *getCommonNNS(ASTContext &Ctx, const T *X,
13467-
const T *Y) {
13468-
// FIXME: Try to keep the common NNS sugar.
13469-
return X->getQualifier() == Y->getQualifier()
13470-
? X->getQualifier()
13471-
: Ctx.getCanonicalNestedNameSpecifier(X->getQualifier());
13611+
static NestedNameSpecifier *getCommonQualifier(ASTContext &Ctx, const T *X,
13612+
const T *Y, bool IsSame) {
13613+
return ::getCommonNNS(Ctx, X->getQualifier(), Y->getQualifier(), IsSame);
1347213614
}
1347313615

1347413616
template <class T>
@@ -13879,8 +14021,9 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
1387914021
*NY = cast<DependentNameType>(Y);
1388014022
assert(NX->getIdentifier() == NY->getIdentifier());
1388114023
return Ctx.getDependentNameType(
13882-
getCommonTypeKeyword(NX, NY), getCommonNNS(Ctx, NX, NY),
13883-
NX->getIdentifier(), NX->getCanonicalTypeInternal());
14024+
getCommonTypeKeyword(NX, NY),
14025+
getCommonQualifier(Ctx, NX, NY, /*IsSame=*/true), NX->getIdentifier(),
14026+
NX->getCanonicalTypeInternal());
1388414027
}
1388514028
case Type::DependentTemplateSpecialization: {
1388614029
const auto *TX = cast<DependentTemplateSpecializationType>(X),
@@ -13889,8 +14032,9 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
1388914032
auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(),
1389014033
TY->template_arguments());
1389114034
return Ctx.getDependentTemplateSpecializationType(
13892-
getCommonTypeKeyword(TX, TY), getCommonNNS(Ctx, TX, TY),
13893-
TX->getIdentifier(), As);
14035+
getCommonTypeKeyword(TX, TY),
14036+
getCommonQualifier(Ctx, TX, TY, /*IsSame=*/true), TX->getIdentifier(),
14037+
As);
1389414038
}
1389514039
case Type::UnaryTransform: {
1389614040
const auto *TX = cast<UnaryTransformType>(X),
@@ -14047,7 +14191,8 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
1404714191
case Type::Elaborated: {
1404814192
const auto *EX = cast<ElaboratedType>(X), *EY = cast<ElaboratedType>(Y);
1404914193
return Ctx.getElaboratedType(
14050-
::getCommonTypeKeyword(EX, EY), ::getCommonNNS(Ctx, EX, EY),
14194+
::getCommonTypeKeyword(EX, EY),
14195+
::getCommonQualifier(Ctx, EX, EY, /*IsSame=*/false),
1405114196
Ctx.getQualifiedType(Underlying),
1405214197
::getCommonDecl(EX->getOwnedTagDecl(), EY->getOwnedTagDecl()));
1405314198
}

clang/test/SemaCXX/sugar-common-types.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ template <class T> struct S1 {
4444
};
4545

4646
N t10 = 0 ? S1<X1>() : S1<Y1>(); // expected-error {{from 'S1<B1>' (aka 'S1<int>')}}
47-
N t11 = 0 ? S1<X1>::S2<X2>() : S1<Y1>::S2<Y2>(); // expected-error {{from 'S1<int>::S2<B2>' (aka 'S2<void>')}}
47+
N t11 = 0 ? S1<X1>::S2<X2>() : S1<Y1>::S2<Y2>(); // expected-error {{from 'S1<B1>::S2<B2>' (aka 'S2<void>')}}
4848

4949
template <class T> using Al = S1<T>;
5050

@@ -88,7 +88,7 @@ N t19 = 0 ? (__underlying_type(EnumsX::X)){} : (__underlying_type(EnumsY::Y)){};
8888
// expected-error@-1 {{rvalue of type 'B1' (aka 'int')}}
8989

9090
N t20 = 0 ? (__underlying_type(EnumsX::X)){} : (__underlying_type(EnumsY::X)){};
91-
// expected-error@-1 {{rvalue of type '__underlying_type(Enums::X)' (aka 'int')}}
91+
// expected-error@-1 {{rvalue of type '__underlying_type(EnumsB::X)' (aka 'int')}}
9292

9393
using QX = const SB1 *;
9494
using QY = const ::SB1 *;

0 commit comments

Comments
 (0)