mirror of
https://github.com/intel/llvm.git
synced 2026-01-28 00:15:21 +08:00
[clang] implement printing of canonical expressions (#135133)
This patch extends the canonicalization printing policy to cover expressions and template names, and wires that up to the template argument printer, covering expressions, and to the expression within a dependent decltype. This is helpful for debugging, or if these expressions somehow end up in diagnostics, as without this patch they can print as completely unrelated expressions, which can be quite confusing. This is because expressions are not uniqued, unlike types, and when a template specialization containing an expression is the first to be canonicalized, the expression ends up appearing in the canonical type of subsequent equivalent specializations. Fixes https://github.com/llvm/llvm-project/issues/92292
This commit is contained in:
@@ -69,7 +69,7 @@ void StaticAccessedThroughInstanceCheck::check(
|
||||
PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true;
|
||||
PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true;
|
||||
|
||||
PrintingPolicyWithSuppressedTag.PrintCanonicalTypes =
|
||||
PrintingPolicyWithSuppressedTag.PrintAsCanonical =
|
||||
!BaseExpr->getType()->isTypedefNameType();
|
||||
|
||||
std::string BaseTypeName =
|
||||
|
||||
@@ -33,7 +33,7 @@ bool MatchesAnyListedTypeNameMatcher::matches(
|
||||
|
||||
PrintingPolicy PrintingPolicyWithSuppressedTag(
|
||||
Finder->getASTContext().getLangOpts());
|
||||
PrintingPolicyWithSuppressedTag.PrintCanonicalTypes = CanonicalTypes;
|
||||
PrintingPolicyWithSuppressedTag.PrintAsCanonical = CanonicalTypes;
|
||||
PrintingPolicyWithSuppressedTag.SuppressElaboration = true;
|
||||
PrintingPolicyWithSuppressedTag.SuppressScope = false;
|
||||
PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true;
|
||||
|
||||
@@ -76,7 +76,7 @@ struct PrintingPolicy {
|
||||
MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true),
|
||||
MSVCFormatting(false), ConstantsAsWritten(false),
|
||||
SuppressImplicitBase(false), FullyQualifiedName(false),
|
||||
PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true),
|
||||
PrintAsCanonical(false), PrintInjectedClassNameWithArguments(true),
|
||||
UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false),
|
||||
CleanUglifiedParameters(false), EntireContentsOfLargeArray(true),
|
||||
UseEnumerators(true), UseHLSLTypes(LO.HLSL) {}
|
||||
@@ -310,9 +310,9 @@ struct PrintingPolicy {
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned FullyQualifiedName : 1;
|
||||
|
||||
/// Whether to print types as written or canonically.
|
||||
/// Whether to print entities as written or canonically.
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned PrintCanonicalTypes : 1;
|
||||
unsigned PrintAsCanonical : 1;
|
||||
|
||||
/// Whether to print an InjectedClassNameType with template arguments or as
|
||||
/// written. When a template argument is unnamed, printing it results in
|
||||
|
||||
@@ -735,7 +735,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
|
||||
llvm::raw_string_ostream POut(Proto);
|
||||
DeclPrinter TArgPrinter(POut, SubPolicy, Context, Indentation);
|
||||
const auto *TArgAsWritten = D->getTemplateSpecializationArgsAsWritten();
|
||||
if (TArgAsWritten && !Policy.PrintCanonicalTypes)
|
||||
if (TArgAsWritten && !Policy.PrintAsCanonical)
|
||||
TArgPrinter.printTemplateArguments(TArgAsWritten->arguments(), nullptr);
|
||||
else if (const TemplateArgumentList *TArgs =
|
||||
D->getTemplateSpecializationArgs())
|
||||
@@ -1124,7 +1124,7 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
|
||||
S->getSpecializedTemplate()->getTemplateParameters();
|
||||
const ASTTemplateArgumentListInfo *TArgAsWritten =
|
||||
S->getTemplateArgsAsWritten();
|
||||
if (TArgAsWritten && !Policy.PrintCanonicalTypes)
|
||||
if (TArgAsWritten && !Policy.PrintAsCanonical)
|
||||
printTemplateArguments(TArgAsWritten->arguments(), TParams);
|
||||
else
|
||||
printTemplateArguments(S->getTemplateArgs().asArray(), TParams);
|
||||
|
||||
@@ -1724,6 +1724,8 @@ void JSONNodeDumper::VisitTemplateExpansionTemplateArgument(
|
||||
void JSONNodeDumper::VisitExpressionTemplateArgument(
|
||||
const TemplateArgument &TA) {
|
||||
JOS.attribute("isExpr", true);
|
||||
if (TA.isCanonicalExpr())
|
||||
JOS.attribute("isCanonical", true);
|
||||
}
|
||||
void JSONNodeDumper::VisitPackTemplateArgument(const TemplateArgument &TA) {
|
||||
JOS.attribute("isPack", true);
|
||||
|
||||
@@ -1305,9 +1305,13 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) {
|
||||
Qualifier->print(OS, Policy);
|
||||
if (Node->hasTemplateKeyword())
|
||||
OS << "template ";
|
||||
|
||||
bool ForceAnonymous =
|
||||
Policy.PrintAsCanonical && VD->getKind() == Decl::NonTypeTemplateParm;
|
||||
DeclarationNameInfo NameInfo = Node->getNameInfo();
|
||||
if (IdentifierInfo *ID = NameInfo.getName().getAsIdentifierInfo();
|
||||
ID || NameInfo.getName().getNameKind() != DeclarationName::Identifier) {
|
||||
!ForceAnonymous &&
|
||||
(ID || NameInfo.getName().getNameKind() != DeclarationName::Identifier)) {
|
||||
if (Policy.CleanUglifiedParameters &&
|
||||
isa<ParmVarDecl, NonTypeTemplateParmDecl>(VD) && ID)
|
||||
OS << ID->deuglifiedName();
|
||||
|
||||
@@ -559,9 +559,12 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
|
||||
printIntegral(*this, Out, Policy, IncludeType);
|
||||
break;
|
||||
|
||||
case Expression:
|
||||
getAsExpr()->printPretty(Out, nullptr, Policy);
|
||||
case Expression: {
|
||||
PrintingPolicy ExprPolicy = Policy;
|
||||
ExprPolicy.PrintAsCanonical = isCanonicalExpr();
|
||||
getAsExpr()->printPretty(Out, nullptr, ExprPolicy);
|
||||
break;
|
||||
}
|
||||
|
||||
case Pack:
|
||||
Out << "<";
|
||||
|
||||
@@ -410,9 +410,9 @@ bool TemplateName::containsUnexpandedParameterPack() const {
|
||||
|
||||
void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
|
||||
Qualified Qual) const {
|
||||
auto handleAnonymousTTP = [](TemplateDecl *TD, raw_ostream &OS) {
|
||||
auto handleAnonymousTTP = [&](TemplateDecl *TD, raw_ostream &OS) {
|
||||
if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(TD);
|
||||
TTP && TTP->getIdentifier() == nullptr) {
|
||||
TTP && (Policy.PrintAsCanonical || TTP->getIdentifier() == nullptr)) {
|
||||
OS << "template-parameter-" << TTP->getDepth() << "-" << TTP->getIndex();
|
||||
return true;
|
||||
}
|
||||
@@ -430,6 +430,8 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
|
||||
// names more often than to export them, thus using the original name is
|
||||
// most useful in this case.
|
||||
TemplateDecl *Template = getAsTemplateDecl();
|
||||
if (Policy.PrintAsCanonical)
|
||||
Template = cast<TemplateDecl>(Template->getCanonicalDecl());
|
||||
if (handleAnonymousTTP(Template, OS))
|
||||
return;
|
||||
if (Qual == Qualified::None)
|
||||
@@ -437,6 +439,10 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
|
||||
else
|
||||
Template->printQualifiedName(OS, Policy);
|
||||
} else if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) {
|
||||
if (Policy.PrintAsCanonical) {
|
||||
QTN->getUnderlyingTemplate().print(OS, Policy, Qual);
|
||||
return;
|
||||
}
|
||||
if (NestedNameSpecifier *NNS = QTN->getQualifier();
|
||||
Qual != Qualified::None && NNS)
|
||||
NNS->print(OS, Policy);
|
||||
|
||||
@@ -1357,6 +1357,8 @@ void TextNodeDumper::VisitTemplateExpansionTemplateArgument(
|
||||
void TextNodeDumper::VisitExpressionTemplateArgument(
|
||||
const TemplateArgument &TA) {
|
||||
OS << " expr";
|
||||
if (TA.isCanonicalExpr())
|
||||
OS << " canonical";
|
||||
dumpTemplateArgument(TA);
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ void TypePrinter::spaceBeforePlaceHolder(raw_ostream &OS) {
|
||||
|
||||
static SplitQualType splitAccordingToPolicy(QualType QT,
|
||||
const PrintingPolicy &Policy) {
|
||||
if (Policy.PrintCanonicalTypes)
|
||||
if (Policy.PrintAsCanonical)
|
||||
QT = QT.getCanonicalType();
|
||||
return QT.split();
|
||||
}
|
||||
@@ -1273,8 +1273,11 @@ void TypePrinter::printTypeOfAfter(const TypeOfType *T, raw_ostream &OS) {}
|
||||
|
||||
void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) {
|
||||
OS << "decltype(";
|
||||
if (T->getUnderlyingExpr())
|
||||
T->getUnderlyingExpr()->printPretty(OS, nullptr, Policy);
|
||||
if (const Expr *E = T->getUnderlyingExpr()) {
|
||||
PrintingPolicy ExprPolicy = Policy;
|
||||
ExprPolicy.PrintAsCanonical = T->isCanonicalUnqualified();
|
||||
E->printPretty(OS, nullptr, ExprPolicy);
|
||||
}
|
||||
OS << ')';
|
||||
spaceBeforePlaceHolder(OS);
|
||||
}
|
||||
@@ -1548,7 +1551,7 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
|
||||
const ASTTemplateArgumentListInfo *TArgAsWritten =
|
||||
S->getTemplateArgsAsWritten();
|
||||
IncludeStrongLifetimeRAII Strong(Policy);
|
||||
if (TArgAsWritten && !Policy.PrintCanonicalTypes)
|
||||
if (TArgAsWritten && !Policy.PrintAsCanonical)
|
||||
printTemplateArgumentList(OS, TArgAsWritten->arguments(), Policy,
|
||||
TParams);
|
||||
else
|
||||
@@ -2422,9 +2425,8 @@ static void
|
||||
printTo(raw_ostream &OS, ArrayRef<TA> Args, const PrintingPolicy &Policy,
|
||||
const TemplateParameterList *TPL, bool IsPack, unsigned ParmIndex) {
|
||||
// Drop trailing template arguments that match default arguments.
|
||||
if (TPL && Policy.SuppressDefaultTemplateArgs &&
|
||||
!Policy.PrintCanonicalTypes && !Args.empty() && !IsPack &&
|
||||
Args.size() <= TPL->size()) {
|
||||
if (TPL && Policy.SuppressDefaultTemplateArgs && !Policy.PrintAsCanonical &&
|
||||
!Args.empty() && !IsPack && Args.size() <= TPL->size()) {
|
||||
llvm::SmallVector<TemplateArgument, 8> OrigArgs;
|
||||
for (const TA &A : Args)
|
||||
OrigArgs.push_back(getArgument(A));
|
||||
|
||||
@@ -287,7 +287,7 @@ PrintingPolicy CGDebugInfo::getPrintingPolicy() const {
|
||||
|
||||
PP.SuppressInlineNamespace =
|
||||
PrintingPolicy::SuppressInlineNamespaceMode::None;
|
||||
PP.PrintCanonicalTypes = true;
|
||||
PP.PrintAsCanonical = true;
|
||||
PP.UsePreferredNames = false;
|
||||
PP.AlwaysIncludeTypeForTemplateArgument = true;
|
||||
PP.UseEnumerators = false;
|
||||
|
||||
@@ -3463,7 +3463,7 @@ Sema::findFailedBooleanCondition(Expr *Cond) {
|
||||
{
|
||||
llvm::raw_string_ostream Out(Description);
|
||||
PrintingPolicy Policy = getPrintingPolicy();
|
||||
Policy.PrintCanonicalTypes = true;
|
||||
Policy.PrintAsCanonical = true;
|
||||
FailedBooleanConditionPrinterHelper Helper(Policy);
|
||||
FailedCond->printPretty(Out, &Helper, Policy, 0, "\n", nullptr);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,10 @@ class Foo {
|
||||
template float Foo::As();
|
||||
template double Foo::As2();
|
||||
|
||||
template<int B1> struct B {};
|
||||
template<class C1> struct C {};
|
||||
template<class D1, int D2> struct D {};
|
||||
|
||||
// Partial ordering with conversion function templates.
|
||||
struct X0 {
|
||||
template<typename T> operator T*() {
|
||||
@@ -64,6 +68,11 @@ struct X0 {
|
||||
}
|
||||
|
||||
template<typename T> operator T*() const; // expected-note{{explicit instantiation refers here}}
|
||||
template<int V> operator B<V>() const; // expected-note{{explicit instantiation refers here}}
|
||||
template<class T, int V> operator C<T[V]>() const; // expected-note{{explicit instantiation refers here}}
|
||||
#if __cplusplus >= 201103L
|
||||
template<int V> operator D<decltype(V), V>() const; // expected-note{{explicit instantiation refers here}}
|
||||
#endif
|
||||
|
||||
template<typename T> operator const T*() const {
|
||||
T x = T();
|
||||
@@ -76,7 +85,15 @@ struct X0 {
|
||||
|
||||
template X0::operator const char*() const; // expected-note{{'X0::operator const char *<char>' requested here}}
|
||||
template X0::operator const int*(); // expected-note{{'X0::operator const int *<const int>' requested here}}
|
||||
// FIXME: These diagnostics are printing canonical types.
|
||||
template X0::operator float*() const; // expected-error{{explicit instantiation of undefined function template 'operator type-parameter-0-0 *'}}
|
||||
template X0::operator B<0>() const; // expected-error {{undefined function template 'operator B<value-parameter-0-0>'}}
|
||||
// FIXME: Within the above issue were we print canonical types here, printing the array
|
||||
// index expression as non-canonical is extra bad.
|
||||
template X0::operator C<int[1]>() const; // expected-error {{undefined function template 'operator C<type-parameter-0-0[V]>'}}
|
||||
#if __cplusplus >= 201103L
|
||||
template X0::operator D<int, 0>() const; // expected-error {{undefined function template 'operator D<decltype(value-parameter-0-0), value-parameter-0-0>'}}
|
||||
#endif
|
||||
|
||||
void test_X0(X0 x0, const X0 &x0c) {
|
||||
x0.operator const int*(); // expected-note{{in instantiation of function template specialization}}
|
||||
|
||||
@@ -76,7 +76,7 @@ TEST(TypePrinter, TemplateId2) {
|
||||
ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "<int>",
|
||||
[](PrintingPolicy &Policy) {
|
||||
Policy.FullyQualifiedName = true;
|
||||
Policy.PrintCanonicalTypes = true;
|
||||
Policy.PrintAsCanonical = true;
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user