[clang] [clang-tools-extra] [lldb] [clang] implement CWG1980 and CWG2064: instantiation-dependency improvements (PR #190495)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 18 19:01:49 PDT 2026
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/190495
>From abad6a8924a40ab7f1b4444a899c29cbfc730e38 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Sat, 4 Apr 2026 20:44:35 -0300
Subject: [PATCH] [clang] implement CWG2064: ignore value dependence for
decltype
The 'decltype' for a value-dependent (but non-type-dependent) should be known,
so this patch makes them non-opaque instead.
This patch also implements what's neceessary to allow overloading
on pure differences in instantiation dependence, making `std::void_t`
usable for SFINAE purposes.
This also readds a few test cases from da98651, which was a previous attempt
at resolving CWG2064.
Fixes #8740
Fixes #61818
Fixes #190388
---
.../clang-tidy/modernize/LoopConvertUtils.cpp | 4 +-
.../clang-tidy/utils/ASTUtils.cpp | 7 +-
clang-tools-extra/clangd/AST.cpp | 6 +-
clang-tools-extra/clangd/InlayHints.cpp | 2 +-
clang/docs/ReleaseNotes.rst | 5 +
clang/include/clang/AST/ASTContext.h | 109 +-
clang/include/clang/AST/DeclTemplate.h | 3 +-
clang/include/clang/AST/DependenceFlags.h | 8 +-
clang/include/clang/AST/Expr.h | 10 +-
clang/include/clang/AST/PropertiesBase.td | 7 +-
clang/include/clang/AST/RecursiveASTVisitor.h | 5 +-
clang/include/clang/AST/Stmt.h | 10 +-
clang/include/clang/AST/TemplateBase.h | 19 +-
clang/include/clang/AST/TextNodeDumper.h | 1 +
clang/include/clang/AST/TypeBase.h | 46 +-
clang/include/clang/AST/TypeLoc.h | 5 +-
clang/include/clang/AST/TypeProperties.td | 5 +-
clang/include/clang/Basic/Specifiers.h | 74 ++
clang/include/clang/Sema/Sema.h | 1 +
.../clang/Serialization/ASTRecordReader.h | 17 +-
.../clang/Serialization/ASTRecordWriter.h | 4 +
clang/lib/AST/ASTContext.cpp | 1079 ++++++++++++++---
clang/lib/AST/ASTDiagnostic.cpp | 22 +-
clang/lib/AST/ASTImporter.cpp | 5 +-
clang/lib/AST/DeclTemplate.cpp | 22 +-
clang/lib/AST/ItaniumMangle.cpp | 246 ++--
clang/lib/AST/JSONNodeDumper.cpp | 12 +-
clang/lib/AST/ODRHash.cpp | 6 +-
clang/lib/AST/StmtProfile.cpp | 72 +-
clang/lib/AST/TemplateBase.cpp | 16 +-
clang/lib/AST/TemplateName.cpp | 7 +-
clang/lib/AST/TextNodeDumper.cpp | 25 +-
clang/lib/AST/Type.cpp | 144 +--
clang/lib/AST/TypePrinter.cpp | 3 +-
.../Frontend/Rewrite/RewriteModernObjC.cpp | 6 +-
clang/lib/Sema/HLSLExternalSemaSource.cpp | 7 +-
clang/lib/Sema/SemaCXXScopeSpec.cpp | 43 +-
clang/lib/Sema/SemaChecking.cpp | 4 +-
clang/lib/Sema/SemaConcept.cpp | 15 +-
clang/lib/Sema/SemaDecl.cpp | 45 +-
clang/lib/Sema/SemaDeclCXX.cpp | 4 +-
clang/lib/Sema/SemaExceptionSpec.cpp | 9 +-
clang/lib/Sema/SemaExpr.cpp | 2 +-
clang/lib/Sema/SemaExprCXX.cpp | 1 +
clang/lib/Sema/SemaExprMember.cpp | 4 +-
clang/lib/Sema/SemaLookup.cpp | 3 +-
clang/lib/Sema/SemaOpenACCAtomic.cpp | 12 +-
clang/lib/Sema/SemaOpenMP.cpp | 52 +-
clang/lib/Sema/SemaOverload.cpp | 45 +-
clang/lib/Sema/SemaTemplate.cpp | 124 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 62 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 23 +-
clang/lib/Sema/SemaTemplateVariadic.cpp | 15 +-
clang/lib/Sema/SemaType.cpp | 4 +-
clang/lib/Sema/TreeTransform.h | 24 +-
clang/lib/Serialization/ASTReader.cpp | 7 +-
clang/lib/Serialization/ASTReaderDecl.cpp | 9 +-
clang/lib/Serialization/ASTWriter.cpp | 1 +
clang/test/AST/ast-dump-templates.cpp | 8 +-
clang/test/CXX/drs/cwg19xx.cpp | 8 +
clang/test/CXX/drs/cwg20xx.cpp | 13 +
.../CXX/temp/temp.decls/temp.alias/p3.cpp | 5 +-
.../test/CXX/temp/temp.decls/temp.mem/p5.cpp | 2 +-
.../test/CodeGenCXX/mangle-exception-spec.cpp | 11 +-
clang/test/CodeGenCXX/mangle-subst.cpp | 10 +-
clang/test/CodeGenCXX/mangle-template.cpp | 37 +-
.../CodeGenCXX/microsoft-abi-default-cc.cpp | 2 +-
clang/test/Sema/array-parameter.cpp | 2 +-
clang/test/Sema/invalid-bitwidth-expr.mm | 1 +
clang/test/SemaCXX/alias-template.cpp | 4 +-
clang/test/SemaCXX/decltype.cpp | 15 +-
clang/test/SemaCXX/source_location.cpp | 7 +-
clang/test/SemaCXX/sugar-common-types.cpp | 18 +-
clang/test/SemaCXX/typeof.cpp | 2 +-
clang/test/SemaTemplate/GH164330.cpp | 2 +-
.../SemaTemplate/concepts-out-of-line-def.cpp | 7 +-
clang/test/SemaTemplate/concepts.cpp | 10 +
clang/test/SemaTemplate/deduction-guide.cpp | 23 +-
clang/test/SemaTemplate/dependent-expr.cpp | 2 +-
.../SemaTemplate/dependent-type-identity.cpp | 8 +-
.../test/SemaTemplate/injected-class-name.cpp | 44 +
.../test/SemaTemplate/instantiate-expr-1.cpp | 10 +
.../SemaTemplate/instantiation-dependence.cpp | 324 ++++-
.../SemaTemplate/partial-spec-instantiate.cpp | 7 +
.../SemaTemplate/temp_arg_nontype_cxx1z.cpp | 18 +-
.../SemaTemplate/temp_arg_template_p0522.cpp | 11 +-
clang/unittests/AST/TypePrinterTest.cpp | 6 +-
lldb/unittests/Symbol/TestTypeSystemClang.cpp | 3 +-
88 files changed, 2373 insertions(+), 775 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
index e9932ba444bc9..2abbeb9df5c54 100644
--- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
@@ -230,7 +230,7 @@ template <typename ContainerT>
static bool containsExpr(ASTContext *Context, const ContainerT *Container,
const Expr *E) {
llvm::FoldingSetNodeID ID;
- E->Profile(ID, *Context, true);
+ E->Profile(ID, *Context, CanonicalizationKind::Structural);
return llvm::any_of(*Container,
[&](const auto &I) { return ID == I.second; });
}
@@ -468,7 +468,7 @@ void ForLoopIndexUseVisitor::addComponents(const ComponentVector &Components) {
void ForLoopIndexUseVisitor::addComponent(const Expr *E) {
llvm::FoldingSetNodeID ID;
const Expr *Node = E->IgnoreParenImpCasts();
- Node->Profile(ID, *Context, true);
+ Node->Profile(ID, *Context, CanonicalizationKind::Structural);
DependentExprs.emplace_back(Node, ID);
}
diff --git a/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp b/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
index 4baef7539008a..d01f15512ff6f 100644
--- a/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
@@ -106,8 +106,11 @@ bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt,
}
llvm::FoldingSetNodeID DataFirst, DataSecond;
- FirstStmt->Profile(DataFirst, Context, Canonical);
- SecondStmt->Profile(DataSecond, Context, Canonical);
+ auto CanonKind =
+ Canonical ? CanonicalizationKindOrNone(CanonicalizationKind::Structural)
+ : std::nullopt;
+ FirstStmt->Profile(DataFirst, Context, CanonKind);
+ SecondStmt->Profile(DataSecond, Context, CanonKind);
return DataFirst == DataSecond;
}
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 2ebc9b49cac55..826f92681865f 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -562,8 +562,12 @@ class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> {
// decltype(I) J = I;
// decltype(J) K = J;
const DecltypeType *DT = dyn_cast<DecltypeType>(TL.getTypePtr());
- while (DT && !DT->getUnderlyingType().isNull()) {
+ while (DT) {
DeducedType = DT->getUnderlyingType();
+ if (DeducedType.isNull()) {
+ DeducedType = DT->getUnderlyingExpr()->getType();
+ break;
+ }
DT = dyn_cast<DecltypeType>(DeducedType.getTypePtr());
}
return true;
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 951177e3546f2..124ee4da08aa4 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -400,7 +400,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
bool VisitTypeLoc(TypeLoc TL) {
if (const auto *DT = llvm::dyn_cast<DecltypeType>(TL.getType()))
- if (QualType UT = DT->getUnderlyingType(); !UT->isDependentType())
+ if (QualType UT = DT->getUnderlyingType(); !UT.isNull())
addTypeHint(TL.getSourceRange(), UT, ": ");
return true;
}
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3e2d287d1eb1f..2dd7814e13b47 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -147,6 +147,11 @@ C++17 Feature Support
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Clang now implements the proposed direction for CWG1980: now it's possible to overload on signatures with
+ differing requirements for instantiation failure, effectively supporting SFINAE on `void_t`. (#GH190388)
+- Clang now implements CWG2064: `decltype(expr)` is not dependent anymore when the value of the expression
+ is dependent. (#GH8740) (#GH61818)
+
C Language Changes
------------------
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index ba1b58489c327..86e1b5340f27f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -256,8 +256,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
FunctionProtoTypes;
mutable llvm::ContextualFoldingSet<DependentTypeOfExprType, ASTContext &>
DependentTypeOfExprTypes;
- mutable llvm::ContextualFoldingSet<DependentDecltypeType, ASTContext &>
- DependentDecltypeTypes;
+ mutable llvm::ContextualFoldingSet<DecltypeType, ASTContext &> DecltypeTypes;
mutable llvm::ContextualFoldingSet<PackIndexingType, ASTContext &>
DependentPackIndexingTypes;
@@ -401,6 +400,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// current file before they are compared locally.
unsigned NextStringLiteralVersion = 0;
+ /// A cache mapping from types to their functionally equivalent canonical
+ /// type.
+ mutable llvm::DenseMap<const Type *, QualType>
+ FunctionallyEquivalentTypeCache;
+
/// MD5 hash of CUID. It is calculated when first used and cached by this
/// data member.
mutable std::string CUIDHash;
@@ -1545,7 +1549,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType NewResultType);
/// Adjust the given function result type.
- CanQualType getCanonicalFunctionResultType(QualType ResultType) const;
+ QualType getCanonicalFunctionResultType(QualType ResultType,
+ CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const;
+ CanQualType getCanonicalFunctionResultType(QualType ResultType) const {
+ bool AnyNonCanonical = false;
+ return CanQualType::CreateUnsafe(getCanonicalFunctionResultType(
+ ResultType, CanonicalizationKind::Structural, AnyNonCanonical));
+ }
/// Change the result type of a function type once it is deduced.
void adjustDeducedFunctionResultType(FunctionDecl *FD, QualType ResultType);
@@ -1975,9 +1986,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
Decl *AssociatedDecl, unsigned Index,
UnsignedOrNone PackIndex,
bool Final) const;
- QualType getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
- unsigned Index, bool Final,
- const TemplateArgument &ArgPack);
+ QualType
+ getSubstTemplateTypeParmPackType(Decl *AssociatedDecl, unsigned Index,
+ bool Final,
+ const TemplateArgument &ArgPack) const;
QualType getSubstBuiltinTemplatePack(const TemplateArgument &ArgPack);
QualType
@@ -1993,7 +2005,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
ArrayRef<TemplateArgument> SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs,
- QualType Underlying = QualType()) const;
+ QualType Underlying = QualType(),
+ bool Unique = false) const;
QualType
getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
@@ -2066,7 +2079,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType getReferenceQualifiedType(const Expr *e) const;
/// C++11 decltype.
- QualType getDecltypeType(Expr *e, QualType UnderlyingType) const;
+ QualType getDecltypeType(Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+ QualType UnderlyingType) const;
QualType getPackIndexingType(QualType Pattern, Expr *IndexExpr,
bool FullySubstituted = false,
@@ -2954,7 +2968,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
///
/// Qualifiers are stripped off, functions are turned into function
/// pointers, and arrays decay one level into pointers.
- CanQualType getCanonicalParamType(QualType T) const;
+ QualType getCanonicalParamType(QualType T, CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const;
+ QualType getCanonicalParamType(QualType T, CanonicalizationKind Kind) const {
+ bool AnyNonCanonical = false;
+ return getCanonicalParamType(T, Kind, AnyNonCanonical);
+ }
+ CanQualType getCanonicalParamType(QualType T) const {
+ return CanQualType::CreateUnsafe(
+ getCanonicalParamType(T, CanonicalizationKind::Structural));
+ }
/// Determine whether the given types \p T1 and \p T2 are equivalent.
static bool hasSameType(QualType T1, QualType T2) {
@@ -2964,8 +2987,27 @@ class ASTContext : public RefCountedBase<ASTContext> {
return getCanonicalType(T1) == getCanonicalType(T2);
}
+ QualType getCanonicalType(QualType QT, CanonicalizationKind Kind) const;
+ QualType getCanonicalType(QualType QT, CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const {
+ QualType R = getCanonicalType(QT, Kind);
+ AnyNonCanonical |= !R.isCanonical();
+ return R;
+ }
+
+ bool hasFunctionallyEquivalentType(QualType T1, QualType T2) const {
+ return hasSameType(T1, T2) &&
+ getCanonicalType(T1, CanonicalizationKind::Functional) ==
+ getCanonicalType(T2, CanonicalizationKind::Functional);
+ }
+ bool isFunctionallyCanonicalType(QualType T) const {
+ return getCanonicalType(T, CanonicalizationKind::Functional) == T;
+ }
+
/// Determine whether the given expressions \p X and \p Y are equivalent.
- bool hasSameExpr(const Expr *X, const Expr *Y) const;
+ bool hasSameExpr(const Expr *X, const Expr *Y,
+ CanonicalizationKindOrNone CanonKind =
+ CanonicalizationKind::Structural) const;
/// Return this type as a completely-unqualified array type,
/// capturing the qualifiers in \p Quals.
@@ -3050,6 +3092,17 @@ class ASTContext : public RefCountedBase<ASTContext> {
CallingConv getDefaultCallingConvention(bool IsVariadic,
bool IsCXXMethod) const;
+ NestedNameSpecifier
+ getCanonicalNestedNameSpecifier(NestedNameSpecifier NNS,
+ CanonicalizationKind CanonKind,
+ bool &AnyNonCanonical) const;
+ NestedNameSpecifier
+ getCanonicalNestedNameSpecifier(NestedNameSpecifier NNS,
+ CanonicalizationKind CanonKind) const {
+ bool AnyNonCanonical = false;
+ return getCanonicalNestedNameSpecifier(NNS, CanonKind, AnyNonCanonical);
+ }
+
/// Retrieves the "canonical" template name that refers to a
/// given template.
///
@@ -3068,8 +3121,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// template name uses the shortest form of the dependent
/// nested-name-specifier, which itself contains all canonical
/// types, values, and templates.
- TemplateName getCanonicalTemplateName(TemplateName Name,
- bool IgnoreDeduced = false) const;
+ TemplateName getCanonicalTemplateName(TemplateName Name, bool IgnoreDeduced,
+ CanonicalizationKind CanonKind,
+ bool &AnyNonCanonical) const;
+ TemplateName getCanonicalTemplateName(
+ TemplateName Name, bool IgnoreDeduced = false,
+ CanonicalizationKind CanonKind = CanonicalizationKind::Structural) const {
+ bool AnyNonCanonical = false;
+ return getCanonicalTemplateName(Name, IgnoreDeduced, CanonKind,
+ AnyNonCanonical);
+ }
/// Determine whether the given template names refer to the same
/// template.
@@ -3118,14 +3179,28 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// The canonical template argument is the simplest template argument
/// (which may be a type, value, expression, or declaration) that
/// expresses the value of the argument.
- TemplateArgument getCanonicalTemplateArgument(const TemplateArgument &Arg)
- const;
+ TemplateArgument getCanonicalTemplateArgument(const TemplateArgument &Arg,
+ CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const;
+ TemplateArgument getCanonicalTemplateArgument(
+ const TemplateArgument &Arg,
+ CanonicalizationKind Kind = CanonicalizationKind::Structural) const {
+ bool AnyNonCanonical = false;
+ return getCanonicalTemplateArgument(Arg, Kind, AnyNonCanonical);
+ }
/// Canonicalize the given template argument list.
///
/// Returns true if any arguments were non-canonical, false otherwise.
- bool
- canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args) const;
+ bool canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args,
+ CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const;
+ bool canonicalizeTemplateArguments(
+ MutableArrayRef<TemplateArgument> Args,
+ CanonicalizationKind Kind = CanonicalizationKind::Structural) const {
+ bool AnyNonCanonical = false;
+ return canonicalizeTemplateArguments(Args, Kind, AnyNonCanonical);
+ }
/// Canonicalize the given TemplateTemplateParmDecl.
TemplateTemplateParmDecl *
@@ -3268,6 +3343,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
// Helper for integer ordering
unsigned getIntegerRank(const Type *T) const;
+ SplitQualType buildFunctionallyEquivalentCanonicalType(const Type *T) const;
+
public:
//===--------------------------------------------------------------------===//
// Type Compatibility Predicates
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index a4a1bb9c13c79..dcf6966f4d068 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -2132,7 +2132,6 @@ class ClassTemplatePartialSpecializationDecl
ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, TemplateParameterList *Params,
ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args,
- CanQualType CanonInjectedTST,
ClassTemplatePartialSpecializationDecl *PrevDecl);
ClassTemplatePartialSpecializationDecl(ASTContext &C)
@@ -2149,7 +2148,7 @@ class ClassTemplatePartialSpecializationDecl
Create(ASTContext &Context, TagKind TK, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
TemplateParameterList *Params, ClassTemplateDecl *SpecializedTemplate,
- ArrayRef<TemplateArgument> Args, CanQualType CanonInjectedTST,
+ ArrayRef<TemplateArgument> Args,
ClassTemplatePartialSpecializationDecl *PrevDecl);
static ClassTemplatePartialSpecializationDecl *
diff --git a/clang/include/clang/AST/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h
index c4395259f0758..739af906a3165 100644
--- a/clang/include/clang/AST/DependenceFlags.h
+++ b/clang/include/clang/AST/DependenceFlags.h
@@ -194,7 +194,13 @@ class Dependence {
TypeDependence type() const {
return translate(V, UnexpandedPack, TypeDependence::UnexpandedPack) |
translate(V, Instantiation, TypeDependence::Instantiation) |
- translate(V, Dependent, TypeDependence::Dependent) |
+ // There's a non-obvious choice here: Should Value dependence be
+ // translated to type dependence or not. After CWG2064, `decltype`
+ // only syntactically depends on the value of the expression. For all
+ // the other use cases where the value is dependended on
+ // semantically, this will be modeled through special purpose type
+ // nodes which are always type dependent anyway.
+ translate(V, Type, TypeDependence::Dependent) |
translate(V, Error, TypeDependence::Error) |
translate(V, VariablyModified, TypeDependence::VariablyModified);
}
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index a0ab599fa82d2..d838334f9b003 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -7483,12 +7483,10 @@ class HLSLOutArgExpr : public Expr {
/// range covered by the expression.
///
/// By default, RecoveryExpr uses dependence-bits to take advantage of existing
-/// machinery to deal with dependent code in C++, e.g. RecoveryExpr is preserved
-/// in `decltype(<broken-expr>)` as part of the `DependentDecltypeType`. In
-/// addition to that, clang does not report most errors on dependent
-/// expressions, so we get rid of bogus errors for free. However, note that
-/// unlike other dependent expressions, RecoveryExpr can be produced in
-/// non-template contexts.
+/// machinery to deal with dependent code in C++. In addition to that, clang
+/// does not report most errors on dependent expressions, so we get rid of bogus
+/// errors for free. However, note that unlike other dependent expressions,
+/// RecoveryExpr can be produced in non-template contexts.
///
/// We will preserve the type in RecoveryExpr when the type is known, e.g.
/// preserving the return type for a broken non-overloaded function call, a
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index fd3cce10be303..5b8e72739702b 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -148,6 +148,7 @@ def TypeOfKind : EnumPropertyType<"TypeOfKind">;
def UInt32 : CountPropertyType<"uint32_t">;
def UInt64 : CountPropertyType<"uint64_t">;
def UnsignedOrNone : PropertyType;
+def CanonicalizationKindOrNone : PropertyType;
def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;
def VectorKind : EnumPropertyType<"VectorKind">;
def TypeCoupledDeclRefInfo : PropertyType;
@@ -905,14 +906,14 @@ let Class = PropertyTypeCase<TemplateArgument, "Expression"> in {
def : Property<"expression", ExprRef> {
let Read = [{ node.getAsExpr() }];
}
- def : Property<"IsCanonical", Bool> {
- let Read = [{ node.isCanonicalExpr() }];
+ def : Property<"CanonKind", CanonicalizationKindOrNone> {
+ let Read = [{ node.getExprCanonKind() }];
}
def : Property<"isDefaulted", Bool> {
let Read = [{ node.getIsDefaulted() }];
}
def : Creator<[{
- return TemplateArgument(expression, IsCanonical, isDefaulted);
+ return TemplateArgument(expression, CanonKind, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Pack"> in {
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 1a14dd2c666b5..e328011b80b6b 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1456,9 +1456,8 @@ DEF_TRAVERSE_TYPELOC(TypeOfType, {
})
// FIXME: location of underlying expr
-DEF_TRAVERSE_TYPELOC(DecltypeType, {
- TRY_TO(TraverseStmt(TL.getTypePtr()->getUnderlyingExpr()));
-})
+DEF_TRAVERSE_TYPELOC(DecltypeType,
+ { TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); })
DEF_TRAVERSE_TYPELOC(PackIndexingType, {
TRY_TO(TraverseType(TL.getPattern()));
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 1711d05a16d93..3fa1a8573d166 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1612,8 +1612,14 @@ class alignas(void *) Stmt {
/// other lambda expressions. When true, the lambda expressions with the same
/// implementation will be considered to be the same. ProfileLambdaExpr should
/// only be true when we try to merge two declarations within modules.
- void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
- bool Canonical, bool ProfileLambdaExpr = false) const;
+ /// \returns The maximum canonicalization kind this expression would have
+ /// profiled identically as.
+ /// FIXME: This is not supported for CanonKind == nullopt yet, and in that
+ /// case nullopt will always be returned.
+ CanonicalizationKindOrNone Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Context,
+ CanonicalizationKindOrNone CanonKind,
+ bool ProfileLambdaExpr = false) const;
/// Calculate a unique representation for a statement that is
/// stable across compiler invocations.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index c0db55ea92cd5..95e5b9f1e9f8a 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -167,8 +167,7 @@ class TemplateArgument {
unsigned Kind : 31;
LLVM_PREFERRED_TYPE(bool)
unsigned IsDefaulted : 1;
- LLVM_PREFERRED_TYPE(bool)
- unsigned IsCanonicalExpr : 1;
+ unsigned ExprCanonKind : 2;
uintptr_t V;
};
union {
@@ -190,7 +189,11 @@ class TemplateArgument {
public:
/// Construct an empty, invalid template argument.
constexpr TemplateArgument()
- : TypeOrValue{Null, /*IsDefaulted=*/0, /*IsCanonicalExpr=*/0, /*V=*/0} {}
+ : TypeOrValue{
+ Null, /*IsDefaulted=*/0,
+ /*ExprCanonKind=*/
+ CanonicalizationKindOrNone(std::nullopt).toInternalRepresentation(),
+ /*V=*/0} {}
/// Construct a template type argument.
TemplateArgument(QualType T, bool isNullPtr = false,
@@ -265,10 +268,11 @@ class TemplateArgument {
/// This form of template argument only occurs in template argument
/// lists used for dependent types and for expression; it will not
/// occur in a non-dependent, canonical template argument list.
- TemplateArgument(Expr *E, bool IsCanonical, bool IsDefaulted = false) {
+ TemplateArgument(Expr *E, CanonicalizationKindOrNone CanonKind,
+ bool IsDefaulted = false) {
TypeOrValue.Kind = Expression;
TypeOrValue.IsDefaulted = IsDefaulted;
- TypeOrValue.IsCanonicalExpr = IsCanonical;
+ TypeOrValue.ExprCanonKind = CanonKind.toInternalRepresentation();
TypeOrValue.V = reinterpret_cast<uintptr_t>(E);
}
@@ -413,9 +417,10 @@ class TemplateArgument {
return reinterpret_cast<Expr *>(TypeOrValue.V);
}
- bool isCanonicalExpr() const {
+ CanonicalizationKindOrNone getExprCanonKind() const {
assert(getKind() == Expression && "Unexpected kind");
- return TypeOrValue.IsCanonicalExpr;
+ return CanonicalizationKindOrNone::fromInternalRepresentation(
+ TypeOrValue.ExprCanonKind);
}
/// Iterator that traverses the elements of a template argument pack.
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 32e83ebb5c8eb..8cf6b9d8fe251 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -334,6 +334,7 @@ class TextNodeDumper
void VisitVectorType(const VectorType *T);
void VisitFunctionType(const FunctionType *T);
void VisitFunctionProtoType(const FunctionProtoType *T);
+ void VisitDecltypeType(const DecltypeType *T);
void VisitUnresolvedUsingType(const UnresolvedUsingType *T);
void VisitUsingType(const UsingType *T);
void VisitTypedefType(const TypedefType *T);
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index a64bbb99b13d2..ba20b2504b765 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -27,6 +27,7 @@
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Linkage.h"
+#include "clang/Basic/OptionalUnsigned.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/PointerAuthOptions.h"
#include "clang/Basic/SourceLocation.h"
@@ -2135,6 +2136,15 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
unsigned AttrKind : 32 - NumTypeBits;
};
+ class DecltypeTypeBitfields {
+ friend class DecltypeType;
+
+ LLVM_PREFERRED_TYPE(TypeBitfields)
+ unsigned : NumTypeBits;
+
+ unsigned ExprCanonKind : 2;
+ };
+
class DeducedTypeBitfields {
friend class DeducedType;
@@ -2361,6 +2371,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
ArrayTypeBitfields ArrayTypeBits;
ConstantArrayTypeBitfields ConstantArrayTypeBits;
AttributedTypeBitfields AttributedTypeBits;
+ DecltypeTypeBitfields DecltypeTypeBits;
DeducedTypeBitfields DeducedTypeBits;
AutoTypeBitfields AutoTypeBits;
TypeOfBitfields TypeOfBits;
@@ -6342,44 +6353,42 @@ class TypeOfType : public Type {
};
/// Represents the type `decltype(expr)` (C++11).
-class DecltypeType : public Type {
+class DecltypeType : public Type, public llvm::FoldingSetNode {
Expr *E;
QualType UnderlyingType;
protected:
friend class ASTContext; // ASTContext creates these.
- DecltypeType(Expr *E, QualType underlyingType, QualType can = QualType());
+ DecltypeType(Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+ QualType UnderlyingType, QualType CanonType = QualType());
public:
Expr *getUnderlyingExpr() const { return E; }
+
QualType getUnderlyingType() const { return UnderlyingType; }
+ CanonicalizationKindOrNone getExprCanonicalizationKind() const {
+ return CanonicalizationKindOrNone::fromInternalRepresentation(
+ DecltypeTypeBits.ExprCanonKind);
+ }
+
/// Remove a single level of sugar.
QualType desugar() const;
/// Returns whether this type directly provides sugar.
bool isSugared() const;
- static bool classof(const Type *T) { return T->getTypeClass() == Decltype; }
-};
-
-/// Internal representation of canonical, dependent
-/// decltype(expr) types.
-///
-/// This class is used internally by the ASTContext to manage
-/// canonical, dependent types, only. Clients will only see instances
-/// of this class via DecltypeType nodes.
-class DependentDecltypeType : public DecltypeType, public llvm::FoldingSetNode {
-public:
- DependentDecltypeType(Expr *E);
-
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
- Profile(ID, Context, getUnderlyingExpr());
+ Profile(ID, Context, getUnderlyingExpr(), getExprCanonicalizationKind(),
+ getUnderlyingType());
}
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
- Expr *E);
+ Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+ QualType UnderlyingType);
+
+ static bool classof(const Type *T) { return T->getTypeClass() == Decltype; }
};
class PackIndexingType final
@@ -7496,7 +7505,8 @@ class TemplateSpecializationType : public TypeWithKeyword,
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx);
static void Profile(llvm::FoldingSetNodeID &ID, ElaboratedTypeKeyword Keyword,
TemplateName T, ArrayRef<TemplateArgument> Args,
- QualType Underlying, const ASTContext &Context);
+ bool IsTypeAlias, QualType Underlying,
+ const ASTContext &Context);
static bool classof(const Type *T) {
return T->getTypeClass() == TemplateSpecialization;
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 24df18dbaace4..92c099b474956 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2275,6 +2275,7 @@ class TypeOfTypeLoc
// FIXME: add LParenLoc, it is tricky to support due to the limitation of
// annotated-decltype token.
struct DecltypeTypeLocInfo {
+ Expr *UnderlyingExpr;
SourceLocation DecltypeLoc;
SourceLocation RParenLoc;
};
@@ -2282,7 +2283,8 @@ class DecltypeTypeLoc
: public ConcreteTypeLoc<UnqualTypeLoc, DecltypeTypeLoc, DecltypeType,
DecltypeTypeLocInfo> {
public:
- Expr *getUnderlyingExpr() const { return getTypePtr()->getUnderlyingExpr(); }
+ Expr *getUnderlyingExpr() const { return getLocalData()->UnderlyingExpr; }
+ void setUnderlyingExpr(Expr *E) { getLocalData()->UnderlyingExpr = E; }
SourceLocation getDecltypeLoc() const { return getLocalData()->DecltypeLoc; }
void setDecltypeLoc(SourceLocation Loc) { getLocalData()->DecltypeLoc = Loc; }
@@ -2295,6 +2297,7 @@ class DecltypeTypeLoc
}
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
+ setUnderlyingExpr(getTypePtr()->getUnderlyingExpr());
setDecltypeLoc(Loc);
setRParenLoc(Loc);
}
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 0f3722b36774a..7425978562491 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -470,9 +470,12 @@ let Class = DecltypeType in {
def : Property<"expression", ExprRef> {
let Read = [{ node->getUnderlyingExpr() }];
}
+ def : Property<"ExprCanonKind", CanonicalizationKindOrNone> {
+ let Read = [{ node->getExprCanonicalizationKind() }];
+ }
def : Creator<[{
- return ctx.getDecltypeType(expression, underlyingType);
+ return ctx.getDecltypeType(expression, ExprCanonKind, underlyingType);
}]>;
}
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 8da6fd4cf454a..7523e452ca474 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -345,6 +345,80 @@ namespace clang {
SD_Dynamic ///< Dynamic storage duration.
};
+ /// Kinds of canonicalization.
+ ///
+ /// Canonicalization is a process for transforming (or viewing) a type or
+ /// expression in a way that ignores certain differences that don't affect
+ /// certain aspects of the semantics of the program.
+ ///
+ /// These kinds of canonicalization are used to represent certain equivalence
+ /// classes, where the classes are themselves ordered, one stronger or a
+ /// subset of the other.
+ enum class CanonicalizationKind : uint8_t {
+ /// This kind of canonicalization ignores any differences that don't affect
+ /// the semantics of the program.
+ /// This never ignores references to template parameters, but is otherwise
+ /// the same as the 'Structural' kind when these references don't occur.
+ /// This preserves enough structure to guarantee that any two types in this
+ /// equivalence class have identical results under the same template
+ /// parameter substitution. Substitution will either succeed for both and
+ /// produce 'Structural'-equivalent types, or it will fail for both, for the
+ /// same set of potential substitutions.
+ ///
+ /// This guarantees that these types will exhibit identical SFINAE behavior.
+ /// But this is a weaker guarantee than the functional equivalence proposed
+ /// in the C++ standard, which would require that types which are not
+ /// functionally-equivalent SFINAE differently in at least one way, which
+ /// is not really feasible to implement.
+ ///
+ /// This relationship follows syntactically from type to type.
+ /// For example, the underlying type of a type alias can contain
+ /// instantiation-dependent-only types which may fail substitution later.
+ /// But this underlying type exists in a separate context, and this failure
+ /// can still happen in that context when the type alias itself is
+ /// instantiated.
+ /// So functional canonicalization can ignore these
+ /// instantiation-dependent-only types, because ignoring them won't change
+ /// the behaviour of the program (it will still be invalid).
+ /// But a typedef can't be ignored completely: It can be referred to using a
+ /// qualified name, and this name qualification can contain
+ /// instantiation-dependent-only types which may fail substitution later.
+ /// This substitution failure can appear only in this spelling of the type,
+ /// so it can't be ignored under functional canonicalization.
+ ///
+ /// This is the kind of canonicalization used to determine whether two
+ /// declarations refer to the same entity, and for mangling.
+ Functional,
+ /// Like 'Functional' canonicalization, but ignores any differences that
+ /// could arise from substitution failures.
+ ///
+ /// By using this kind of canonicalization, it is implicitly assumed that
+ /// substitution for any referred template parameters will be valid.
+ ///
+ /// For a template type alias where some template parameters are unused,
+ /// any template arguments to these parameters will be ignored under this
+ /// canonicalization, unlike 'Functional' canonicalization where they would
+ /// be considered part of the type.
+ ///
+ /// This is a superset of 'Functional' canonicalization: all elements which
+ /// are equivalent under 'Functional' canonicalization are also equivalent
+ /// under 'Structural' canonicalization, but not vice versa.
+ ///
+ /// This is the kind of canonicalization used to determine whether two
+ /// template specializations refer to the same entity.
+ Structural,
+ };
+
+ /// This compares CanonicalizationKinds such that 'Structural' is greater than
+ /// 'Functional'.
+ constexpr bool operator<(CanonicalizationKind LHS, CanonicalizationKind RHS) {
+ return llvm::to_underlying(LHS) < llvm::to_underlying(RHS);
+ }
+
+ /// An optional CanonicalizationKind. Following standard convention, a nullopt
+ /// is considered lesser than any value.
+ using CanonicalizationKindOrNone = OptionalUnsigned<CanonicalizationKind>;
+
/// Describes the nullability of a particular type.
enum class NullabilityKind : uint8_t {
/// Values of this type can never be null.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 760555d9c8b9b..cd34b6276f935 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10268,6 +10268,7 @@ class Sema final : public SemaBase {
/// If `Reversed` is true, the parameters of `NewType` will be compared in
/// reverse order. That's useful if one of the functions is being used as a
/// C++20 synthesized operator overload with a reversed parameter order.
+ /// The parameter types are assumed to be functionally-canonicalized.
bool FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
ArrayRef<QualType> New,
unsigned *ArgPos = nullptr,
diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h
index aed1b7d309001..caba5917721cb 100644
--- a/clang/include/clang/Serialization/ASTRecordReader.h
+++ b/clang/include/clang/Serialization/ASTRecordReader.h
@@ -233,11 +233,10 @@ class ASTRecordReader
/// Read a template argument, advancing Idx. (inherited)
// TemplateArgument readTemplateArgument();
using DataStreamBasicReader::readTemplateArgument;
- TemplateArgument readTemplateArgument(bool Canonicalize) {
+ TemplateArgument readTemplateArgument(CanonicalizationKindOrNone CanonKind) {
TemplateArgument Arg = readTemplateArgument();
- if (Canonicalize) {
- Arg = getContext().getCanonicalTemplateArgument(Arg);
- }
+ if (CanonKind)
+ Arg = getContext().getCanonicalTemplateArgument(Arg, *CanonKind);
return Arg;
}
@@ -245,8 +244,9 @@ class ASTRecordReader
TemplateParameterList *readTemplateParameterList();
/// Read a template argument array, advancing Idx.
- void readTemplateArgumentList(SmallVectorImpl<TemplateArgument> &TemplArgs,
- bool Canonicalize = false);
+ void
+ readTemplateArgumentList(SmallVectorImpl<TemplateArgument> &TemplArgs,
+ CanonicalizationKindOrNone CanonKind = std::nullopt);
/// Read a UnresolvedSet structure, advancing Idx.
void readUnresolvedSet(LazyASTUnresolvedSet &Set);
@@ -324,6 +324,11 @@ class ASTRecordReader
return UnsignedOrNone::fromInternalRepresentation(unsigned(readInt()));
}
+ CanonicalizationKindOrNone readCanonicalizationKindOrNone() {
+ return CanonicalizationKindOrNone::fromInternalRepresentation(
+ unsigned(readInt()));
+ }
+
/// Read a string, advancing Idx.
std::string readString() {
return Reader->ReadString(Record, Idx);
diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h
index 9849ea6b395ab..43e09d1682622 100644
--- a/clang/include/clang/Serialization/ASTRecordWriter.h
+++ b/clang/include/clang/Serialization/ASTRecordWriter.h
@@ -193,6 +193,10 @@ class ASTRecordWriter
Record->push_back(Value.toInternalRepresentation());
}
+ void writeCanonicalizationKindOrNone(CanonicalizationKindOrNone Value) {
+ Record->push_back(Value.toInternalRepresentation());
+ }
+
/// Emit an integral value.
void AddAPInt(const llvm::APInt &Value) {
writeAPInt(Value);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ee7f823b014b2..35b388ce13db6 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -886,7 +886,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
DependentAddressSpaceTypes(this_()), DependentVectorTypes(this_()),
DependentSizedMatrixTypes(this_()),
FunctionProtoTypes(this_(), FunctionProtoTypesLog2InitSize),
- DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()),
+ DependentTypeOfExprTypes(this_()), DecltypeTypes(this_()),
DependentPackIndexingTypes(this_()), TemplateSpecializationTypes(this_()),
DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()),
DeducedTemplates(this_()), ArrayParameterTypes(this_()),
@@ -3172,21 +3172,33 @@ ASTContext::getASTObjCInterfaceLayout(const ObjCInterfaceDecl *D) const {
static auto getCanonicalTemplateArguments(const ASTContext &C,
ArrayRef<TemplateArgument> Args,
- bool &AnyNonCanonArgs) {
+ bool &AnyChanged,
+ CanonicalizationKind Kind,
+ bool &AnyNonCanonical) {
SmallVector<TemplateArgument, 16> CanonArgs(Args);
- AnyNonCanonArgs |= C.canonicalizeTemplateArguments(CanonArgs);
+ AnyChanged |=
+ C.canonicalizeTemplateArguments(CanonArgs, Kind, AnyNonCanonical);
return CanonArgs;
}
+static auto getCanonicalTemplateArguments(const ASTContext &C,
+ ArrayRef<TemplateArgument> Args,
+ bool &AnyChanged) {
+ bool AnyNonCanonical = false;
+ return getCanonicalTemplateArguments(
+ C, Args, AnyChanged, CanonicalizationKind::Structural, AnyNonCanonical);
+}
+
bool ASTContext::canonicalizeTemplateArguments(
- MutableArrayRef<TemplateArgument> Args) const {
- bool AnyNonCanonArgs = false;
+ MutableArrayRef<TemplateArgument> Args, CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const {
+ bool AnyChanged = false;
for (auto &Arg : Args) {
TemplateArgument OrigArg = Arg;
- Arg = getCanonicalTemplateArgument(Arg);
- AnyNonCanonArgs |= !Arg.structurallyEquals(OrigArg);
+ Arg = getCanonicalTemplateArgument(Arg, Kind, AnyNonCanonical);
+ AnyChanged |= !Arg.structurallyEquals(OrigArg);
}
- return AnyNonCanonArgs;
+ return AnyChanged;
}
//===----------------------------------------------------------------------===//
@@ -4921,16 +4933,17 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy,
return QualType(New, 0);
}
-CanQualType
-ASTContext::getCanonicalFunctionResultType(QualType ResultType) const {
- CanQualType CanResultType = getCanonicalType(ResultType);
+QualType
+ASTContext::getCanonicalFunctionResultType(QualType ResultType,
+ CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const {
+ QualType CanResultType = getCanonicalType(ResultType, Kind, AnyNonCanonical);
+ Qualifiers Qs = CanResultType.getQualifiers();
// Canonical result types do not have ARC lifetime qualifiers.
- if (CanResultType.getQualifiers().hasObjCLifetime()) {
- Qualifiers Qs = CanResultType.getQualifiers();
+ if (Qs.hasObjCLifetime()) {
Qs.removeObjCLifetime();
- return CanQualType::CreateUnsafe(
- getQualifiedType(CanResultType.getUnqualifiedType(), Qs));
+ CanResultType = getQualifiedType(CanResultType.getUnqualifiedType(), Qs);
}
return CanResultType;
@@ -4970,6 +4983,67 @@ static bool isCanonicalExceptionSpecification(
return false;
}
+static FunctionProtoType::ExceptionSpecInfo
+getCanonicalExceptionSpec(const ASTContext &Ctx,
+ FunctionProtoType::ExceptionSpecInfo ESI,
+ SmallVectorImpl<QualType> &ExceptionTypeStorage,
+ CanonicalizationKind Kind, bool &AnyNonCanonical) {
+ bool NoexceptInType = Ctx.getLangOpts().CPlusPlus17;
+ if (Kind == CanonicalizationKind::Structural && !NoexceptInType)
+ return {};
+ switch (ESI.Type) {
+ case EST_Unparsed:
+ case EST_Unevaluated:
+ case EST_Uninstantiated:
+ // We don't know yet. It shouldn't matter what we pick here; no-one
+ // should ever look at this.
+ [[fallthrough]];
+ case EST_None:
+ case EST_MSAny:
+ case EST_NoexceptFalse:
+ ESI.Type = EST_None;
+ break;
+
+ // A dynamic exception specification is almost always "not noexcept",
+ // with the exception that a pack expansion might expand to no types.
+ case EST_Dynamic: {
+ bool AnyPacks = false, AnyInstantiationDependent = false;
+ for (QualType ET : ESI.Exceptions) {
+ AnyPacks |= ET->getAsCanonical<PackExpansionType>() != nullptr;
+ AnyInstantiationDependent |= ET->isInstantiationDependentType();
+ ExceptionTypeStorage.push_back(
+ Ctx.getCanonicalType(ET, Kind, AnyNonCanonical));
+ }
+ assert((!AnyPacks || AnyInstantiationDependent) &&
+ "pack expansion should be instantiation-dependent");
+ bool DynamicExceptionIsCanonical = NoexceptInType && AnyPacks;
+ if (DynamicExceptionIsCanonical ||
+ (Kind == CanonicalizationKind::Functional &&
+ AnyInstantiationDependent)) {
+ ESI.Type = EST_Dynamic;
+ ESI.Exceptions = ExceptionTypeStorage;
+ AnyNonCanonical |= !DynamicExceptionIsCanonical;
+ } else {
+ ESI.Type = EST_None;
+ }
+ break;
+ }
+
+ case EST_DynamicNone:
+ case EST_BasicNoexcept:
+ case EST_NoexceptTrue:
+ case EST_NoThrow:
+ ESI.Type = NoexceptInType ? EST_BasicNoexcept : EST_None;
+ break;
+
+ case EST_DependentNoexcept:
+ AnyNonCanonical |= !NoexceptInType;
+ // dependent noexcept is already canonical
+ break;
+ }
+ return ESI;
+}
+
QualType ASTContext::getFunctionTypeInternal(
QualType ResultTy, ArrayRef<QualType> ArgArray,
const FunctionProtoType::ExtProtoInfo &EPI, bool OnlyWantCanonical) const {
@@ -5033,48 +5107,11 @@ QualType ASTContext::getFunctionTypeInternal(
FunctionProtoType::ExtProtoInfo CanonicalEPI = EPI;
CanonicalEPI.HasTrailingReturn = false;
- if (IsCanonicalExceptionSpec) {
- // Exception spec is already OK.
- } else if (NoexceptInType) {
- switch (EPI.ExceptionSpec.Type) {
- case EST_Unparsed: case EST_Unevaluated: case EST_Uninstantiated:
- // We don't know yet. It shouldn't matter what we pick here; no-one
- // should ever look at this.
- [[fallthrough]];
- case EST_None: case EST_MSAny: case EST_NoexceptFalse:
- CanonicalEPI.ExceptionSpec.Type = EST_None;
- break;
-
- // A dynamic exception specification is almost always "not noexcept",
- // with the exception that a pack expansion might expand to no types.
- case EST_Dynamic: {
- bool AnyPacks = false;
- for (QualType ET : EPI.ExceptionSpec.Exceptions) {
- if (ET->getAs<PackExpansionType>())
- AnyPacks = true;
- ExceptionTypeStorage.push_back(getCanonicalType(ET));
- }
- if (!AnyPacks)
- CanonicalEPI.ExceptionSpec.Type = EST_None;
- else {
- CanonicalEPI.ExceptionSpec.Type = EST_Dynamic;
- CanonicalEPI.ExceptionSpec.Exceptions = ExceptionTypeStorage;
- }
- break;
- }
-
- case EST_DynamicNone:
- case EST_BasicNoexcept:
- case EST_NoexceptTrue:
- case EST_NoThrow:
- CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
- break;
-
- case EST_DependentNoexcept:
- llvm_unreachable("dependent noexcept is already canonical");
- }
- } else {
- CanonicalEPI.ExceptionSpec = FunctionProtoType::ExceptionSpecInfo();
+ if (!IsCanonicalExceptionSpec) {
+ bool AnyNonCanonical = false;
+ CanonicalEPI.ExceptionSpec = getCanonicalExceptionSpec(
+ *this, EPI.ExceptionSpec, ExceptionTypeStorage,
+ CanonicalizationKind::Structural, AnyNonCanonical);
}
// Adjust the canonical function result type.
@@ -5861,10 +5898,9 @@ QualType ASTContext::getSubstTemplateTypeParmType(QualType Replacement,
return QualType(SubstParm, 0);
}
-QualType
-ASTContext::getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
- unsigned Index, bool Final,
- const TemplateArgument &ArgPack) {
+QualType ASTContext::getSubstTemplateTypeParmPackType(
+ Decl *AssociatedDecl, unsigned Index, bool Final,
+ const TemplateArgument &ArgPack) const {
#ifndef NDEBUG
for (const auto &P : ArgPack.pack_elements())
assert(P.getKind() == TemplateArgument::Type && "Pack contains a non-type");
@@ -6041,8 +6077,8 @@ QualType ASTContext::getCanonicalTemplateSpecializationType(
#endif
llvm::FoldingSetNodeID ID;
- TemplateSpecializationType::Profile(ID, Keyword, Template, Args, QualType(),
- *this);
+ TemplateSpecializationType::Profile(ID, Keyword, Template, Args,
+ /*IsTypeAlias=*/false, QualType(), *this);
void *InsertPos = nullptr;
if (auto *T = TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(T, 0);
@@ -6063,9 +6099,27 @@ QualType ASTContext::getCanonicalTemplateSpecializationType(
QualType ASTContext::getTemplateSpecializationType(
ElaboratedTypeKeyword Keyword, TemplateName Template,
ArrayRef<TemplateArgument> SpecifiedArgs,
- ArrayRef<TemplateArgument> CanonicalArgs, QualType Underlying) const {
- const auto *TD = Template.getAsTemplateDecl(/*IgnoreDeduced=*/true);
- bool IsTypeAlias = TD && TD->isTypeAlias();
+ ArrayRef<TemplateArgument> CanonicalArgs, QualType Underlying,
+ bool Unique) const {
+
+ bool IsTypeAlias = false;
+ if (!Underlying.isNull()) {
+ const auto *TD = Template.getAsTemplateDecl(/*IgnoreDeduced=*/true);
+ IsTypeAlias = TD && TD->isTypeAlias();
+ if (!IsTypeAlias)
+ Underlying = getCanonicalType(Underlying);
+ }
+
+ llvm::FoldingSetNodeID ID;
+ void *InsertPos = nullptr;
+ if (Unique) {
+ TemplateSpecializationType::Profile(ID, Keyword, Template, SpecifiedArgs,
+ IsTypeAlias, Underlying, *this);
+ if (auto *T =
+ TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos))
+ return QualType(T, 0);
+ }
+
if (Underlying.isNull()) {
TemplateName CanonTemplate =
getCanonicalTemplateName(Template, /*IgnoreDeduced=*/true);
@@ -6087,18 +6141,16 @@ QualType ASTContext::getTemplateSpecializationType(
});
}
- // We can get here with an alias template when the specialization
- // contains a pack expansion that does not match up with a parameter
- // pack, or a builtin template which cannot be resolved due to dependency.
- assert((!isa_and_nonnull<TypeAliasTemplateDecl>(TD) ||
- hasAnyPackExpansions(CanonicalArgs)) &&
- "Caller must compute aliased type");
- IsTypeAlias = false;
-
Underlying = getCanonicalTemplateSpecializationType(
CanonKeyword, CanonTemplate, CanonicalArgs);
if (!NonCanonical)
return Underlying;
+
+ if (Unique) {
+ [[maybe_unused]] auto *T =
+ TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos);
+ assert(!T && "broken canonical type");
+ }
}
void *Mem = Allocate(sizeof(TemplateSpecializationType) +
sizeof(TemplateArgument) * SpecifiedArgs.size() +
@@ -6107,6 +6159,8 @@ QualType ASTContext::getTemplateSpecializationType(
auto *Spec = new (Mem) TemplateSpecializationType(
Keyword, Template, IsTypeAlias, SpecifiedArgs, Underlying);
Types.push_back(Spec);
+ if (Unique)
+ TemplateSpecializationTypes.InsertNode(Spec, InsertPos);
return QualType(Spec, 0);
}
@@ -6209,7 +6263,7 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) const {
if (NTTP->isParameterPack())
E = new (*this) PackExpansionExpr(E, NTTP->getLocation(), std::nullopt);
- Arg = TemplateArgument(E, /*IsCanonical=*/false);
+ Arg = TemplateArgument(E, /*CanonKind=*/std::nullopt);
} else {
auto *TTP = cast<TemplateTemplateParmDecl>(Param);
TemplateName Name = getQualifiedTemplateName(
@@ -6690,34 +6744,35 @@ QualType ASTContext::getReferenceQualifiedType(const Expr *E) const {
/// nodes. This would never be helpful, since each such type has its own
/// expression, and would not give a significant memory saving, since there
/// is an Expr tree under each such type.
-QualType ASTContext::getDecltypeType(Expr *E, QualType UnderlyingType) const {
- // C++11 [temp.type]p2:
- // If an expression e involves a template parameter, decltype(e) denotes a
- // unique dependent type. Two such decltype-specifiers refer to the same
- // type only if their expressions are equivalent (14.5.6.1).
- QualType CanonType;
- if (!E->isInstantiationDependent()) {
- CanonType = getCanonicalType(UnderlyingType);
- } else if (!UnderlyingType.isNull()) {
- CanonType = getDecltypeType(E, QualType());
- } else {
- llvm::FoldingSetNodeID ID;
- DependentDecltypeType::Profile(ID, *this, E);
+QualType ASTContext::getDecltypeType(Expr *E,
+ CanonicalizationKindOrNone ExprCanonKind,
+ QualType UnderlyingType) const {
+ llvm::FoldingSetNodeID ID;
+ DecltypeType::Profile(ID, *this, E, ExprCanonKind, UnderlyingType);
- void *InsertPos = nullptr;
- if (DependentDecltypeType *Canon =
- DependentDecltypeTypes.FindNodeOrInsertPos(ID, InsertPos))
- return QualType(Canon, 0);
+ void *InsertPos = nullptr;
+ if (auto *T = DecltypeTypes.FindNodeOrInsertPos(ID, InsertPos))
+ return QualType(T, 0);
- // Build a new, canonical decltype(expr) type.
- auto *DT =
- new (*this, alignof(DependentDecltypeType)) DependentDecltypeType(E);
- DependentDecltypeTypes.InsertNode(DT, InsertPos);
- Types.push_back(DT);
- return QualType(DT, 0);
+ // C++26 [temp.type]p4: If an expression e is type-dependent, decltype(e)
+ // denotes a unique dependent type. Two such decltype-specifiers refer to the
+ // same type only if their expressions are equivalent ([temp.over.link]).
+ QualType CanonType;
+ if (!E->isTypeDependent()) {
+ CanonType = UnderlyingType.getCanonicalType();
+ } else if (ExprCanonKind != CanonicalizationKind::Structural) {
+ UnderlyingType = QualType();
+ CanonType =
+ getDecltypeType(E, CanonicalizationKind::Structural, QualType());
+ // Find the insertion position again.
+ [[maybe_unused]] auto *DT =
+ DecltypeTypes.FindNodeOrInsertPos(ID, InsertPos);
+ assert(!DT && "broken canonicalization");
}
+
auto *DT = new (*this, alignof(DecltypeType))
- DecltypeType(E, UnderlyingType, CanonType);
+ DecltypeType(E, ExprCanonKind, UnderlyingType, CanonType);
+ DecltypeTypes.InsertNode(DT, InsertPos);
Types.push_back(DT);
return QualType(DT, 0);
}
@@ -7040,24 +7095,49 @@ QualType ASTContext::getProcessIDType() const {
// Type Operators
//===----------------------------------------------------------------------===//
-CanQualType ASTContext::getCanonicalParamType(QualType T) const {
+QualType ASTContext::getCanonicalParamType(QualType T,
+ CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const {
// Push qualifiers into arrays, and then discard any remaining
// qualifiers.
- T = getCanonicalType(T);
+ if (Kind == CanonicalizationKind::Functional)
+ if (const auto *Ty = dyn_cast<DecayedType>(T))
+ T = Ty->getOriginalType();
+ T = getCanonicalType(T, Kind, AnyNonCanonical);
T = getVariableArrayDecayedType(T);
- const Type *Ty = T.getTypePtr();
- QualType Result;
- if (getLangOpts().HLSL && isa<ConstantArrayType>(Ty)) {
- Result = getArrayParameterType(QualType(Ty, 0));
- } else if (isa<ArrayType>(Ty)) {
- Result = getArrayDecayedType(QualType(Ty,0));
- } else if (isa<FunctionType>(Ty)) {
- Result = getPointerType(QualType(Ty, 0));
- } else {
- Result = QualType(Ty, 0);
+ if (getLangOpts().HLSL && isa<ConstantArrayType>(T))
+ return getArrayParameterType(T);
+ if (isa<ArrayType>(T)) {
+ if (Kind == CanonicalizationKind::Functional) {
+ switch (T->getTypeClass()) {
+ case Type::ConstantArray:
+ if (const Expr *E = cast<ConstantArrayType>(T)->getSizeExpr();
+ E && E->isInstantiationDependent()) {
+ AnyNonCanonical = true;
+ return T.getUnqualifiedType();
+ }
+ break;
+ case Type::VariableArray:
+ if (const Expr *E = cast<VariableArrayType>(T)->getSizeExpr();
+ E && E->isInstantiationDependent()) {
+ AnyNonCanonical = true;
+ return T.getUnqualifiedType();
+ }
+ break;
+ case Type::DependentSizedArray: {
+ AnyNonCanonical = true;
+ return T.getUnqualifiedType();
+ }
+ default:
+ break;
+ }
+ }
+ return getArrayDecayedType(T);
}
-
- return CanQualType::CreateUnsafe(Result);
+ T = T.getUnqualifiedType();
+ if (isa<FunctionType>(T))
+ return getPointerType(T);
+ return T;
}
QualType ASTContext::getUnqualifiedArrayType(QualType type,
@@ -7324,12 +7404,10 @@ getDefaultTemplateArgumentOrNone(const NamedDecl *P) {
}
}
-TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
- bool IgnoreDeduced) const {
- while (std::optional<TemplateName> UnderlyingOrNone =
- Name.desugar(IgnoreDeduced))
- Name = *UnderlyingOrNone;
-
+TemplateName
+ASTContext::getCanonicalTemplateName(TemplateName Name, bool IgnoreDeduced,
+ CanonicalizationKind CanonKind,
+ bool &AnyNonCanonical) const {
switch (Name.getKind()) {
case TemplateName::Template: {
TemplateDecl *Template = Name.getAsTemplateDecl();
@@ -7351,7 +7429,8 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
DependentTemplateName *DTN = Name.getAsDependentTemplateName();
assert(DTN && "Non-dependent template names must refer to template decls.");
NestedNameSpecifier Qualifier = DTN->getQualifier();
- NestedNameSpecifier CanonQualifier = Qualifier.getCanonical();
+ NestedNameSpecifier CanonQualifier =
+ getCanonicalNestedNameSpecifier(Qualifier, CanonKind, AnyNonCanonical);
if (Qualifier != CanonQualifier || !DTN->hasTemplateKeyword())
return getDependentTemplateName({CanonQualifier, DTN->getName(),
/*HasTemplateKeyword=*/true});
@@ -7361,23 +7440,25 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
case TemplateName::SubstTemplateTemplateParmPack: {
SubstTemplateTemplateParmPackStorage *subst =
Name.getAsSubstTemplateTemplateParmPack();
- TemplateArgument canonArgPack =
- getCanonicalTemplateArgument(subst->getArgumentPack());
+ TemplateArgument canonArgPack = getCanonicalTemplateArgument(
+ subst->getArgumentPack(), CanonKind, AnyNonCanonical);
return getSubstTemplateTemplateParmPack(
canonArgPack, subst->getAssociatedDecl()->getCanonicalDecl(),
subst->getIndex(), subst->getFinal());
}
case TemplateName::DeducedTemplate: {
- assert(IgnoreDeduced == false);
DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName();
- DefaultArguments DefArgs = DTS->getDefaultArguments();
TemplateName Underlying = DTS->getUnderlying();
+ if (IgnoreDeduced)
+ return getCanonicalTemplateName(Underlying, IgnoreDeduced, CanonKind,
+ AnyNonCanonical);
+ DefaultArguments DefArgs = DTS->getDefaultArguments();
- TemplateName CanonUnderlying =
- getCanonicalTemplateName(Underlying, /*IgnoreDeduced=*/true);
- bool NonCanonical = CanonUnderlying != Underlying;
+ TemplateName CanonUnderlying = getCanonicalTemplateName(
+ Underlying, /*IgnoreDeduced=*/true, CanonKind, AnyNonCanonical);
+ bool AnyChanged = CanonUnderlying != Underlying;
auto CanonArgs =
- getCanonicalTemplateArguments(*this, DefArgs.Args, NonCanonical);
+ getCanonicalTemplateArguments(*this, DefArgs.Args, AnyChanged);
ArrayRef<NamedDecl *> Params =
CanonUnderlying.getAsTemplateDecl()->getTemplateParameters()->asArray();
@@ -7398,17 +7479,50 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
// Keep popping from the back any deault arguments which are the same.
if (I == int(CanonArgs.size() - 1))
CanonArgs.pop_back();
- NonCanonical = true;
+ AnyChanged = true;
+ }
+ return AnyChanged ? getDeducedTemplateName(
+ CanonUnderlying,
+ /*DefaultArgs=*/{DefArgs.StartPos, CanonArgs})
+ : Name;
+ }
+ case TemplateName::QualifiedTemplate: {
+ auto *S = Name.getAsQualifiedTemplateName();
+ TemplateName Underlying = S->getUnderlyingTemplate();
+ if (CanonKind == CanonicalizationKind::Structural) {
+ return getCanonicalTemplateName(S->getUnderlyingTemplate(), IgnoreDeduced,
+ CanonKind, AnyNonCanonical);
+ }
+ // The qualifier needs to be attached to whatever entity it belongs to.
+ // Don't simply canonicalize the Underlying template, as that can turn a
+ // UsingShadowDecl into it's target, forming a nonsensical qualified name.
+ switch (Underlying.getKind()) {
+ case TemplateName::Template:
+ Underlying = TemplateName(cast<TemplateDecl>(
+ Underlying.getAsTemplateDecl()->getCanonicalDecl()));
+ break;
+ case TemplateName::UsingTemplate:
+ Underlying = TemplateName(cast<UsingShadowDecl>(
+ Underlying.getAsUsingShadowDecl()->getCanonicalDecl()));
+ break;
+ default:
+ llvm_unreachable(
+ "unexpected underlying template name kind for qualified template");
}
- return NonCanonical ? getDeducedTemplateName(
- CanonUnderlying,
- /*DefaultArgs=*/{DefArgs.StartPos, CanonArgs})
- : Name;
+ NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+ S->getQualifier(), CanonKind, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ return Underlying;
+ return getQualifiedTemplateName(Qualifier, /*TemplateKeyword=*/false,
+ Underlying);
}
case TemplateName::UsingTemplate:
- case TemplateName::QualifiedTemplate:
+ return TemplateName(cast<TemplateDecl>(
+ Name.getAsUsingShadowDecl()->getTargetDecl()->getCanonicalDecl()));
case TemplateName::SubstTemplateTemplateParm:
- llvm_unreachable("always sugar node");
+ return getCanonicalTemplateName(
+ Name.getAsSubstTemplateTemplateParm()->getReplacement(), IgnoreDeduced,
+ CanonKind, AnyNonCanonical);
}
llvm_unreachable("bad template name!");
@@ -7438,8 +7552,10 @@ bool ASTContext::isSameConstraintExpr(const Expr *XCE, const Expr *YCE) const {
return true;
llvm::FoldingSetNodeID XCEID, YCEID;
- XCE->Profile(XCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true);
- YCE->Profile(YCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true);
+ XCE->Profile(XCEID, *this, CanonicalizationKind::Functional,
+ /*ProfileLambdaExpr=*/true);
+ YCE->Profile(YCEID, *this, CanonicalizationKind::Functional,
+ /*ProfileLambdaExpr=*/true);
return XCEID == YCEID;
}
@@ -7551,9 +7667,10 @@ bool ASTContext::isSameDefaultTemplateArgument(const NamedDecl *X,
NTTPX->getDefaultArgument().getArgument().getAsExpr()->IgnoreImpCasts();
Expr *DefaultArgumentY =
NTTPY->getDefaultArgument().getArgument().getAsExpr()->IgnoreImpCasts();
+ // FIXME: This should probably use functional equivalence instead.
llvm::FoldingSetNodeID XID, YID;
- DefaultArgumentX->Profile(XID, *this, /*Canonical=*/true);
- DefaultArgumentY->Profile(YID, *this, /*Canonical=*/true);
+ DefaultArgumentX->Profile(XID, *this, CanonicalizationKind::Structural);
+ DefaultArgumentY->Profile(YID, *this, CanonicalizationKind::Structural);
return XID == YID;
}
@@ -7636,8 +7753,10 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A,
Cand1ID.clear();
Cand2ID.clear();
- (*Cand1A)->getCond()->Profile(Cand1ID, A->getASTContext(), true);
- (*Cand2A)->getCond()->Profile(Cand2ID, B->getASTContext(), true);
+ (*Cand1A)->getCond()->Profile(Cand1ID, A->getASTContext(),
+ CanonicalizationKind::Structural);
+ (*Cand2A)->getCond()->Profile(Cand2ID, B->getASTContext(),
+ CanonicalizationKind::Structural);
// Return false if any of the enable_if expressions of A and B are
// different.
@@ -7888,51 +8007,71 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
}
TemplateArgument
-ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
+ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg,
+ CanonicalizationKind Kind,
+ bool &AnyNonCanonical) const {
switch (Arg.getKind()) {
case TemplateArgument::Null:
return Arg;
- case TemplateArgument::Expression:
- return TemplateArgument(Arg.getAsExpr(), /*IsCanonical=*/true,
- Arg.getIsDefaulted());
-
- case TemplateArgument::Declaration: {
- auto *D = cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl());
- return TemplateArgument(D, getCanonicalType(Arg.getParamTypeForDecl()),
- Arg.getIsDefaulted());
+ case TemplateArgument::Expression: {
+ auto CanonKind =
+ std::max(CanonicalizationKindOrNone(Kind), Arg.getExprCanonKind());
+ // If we are asked to produce a functionally equivalent template argument,
+ // try to downgrade the canonicalization kind to equivalent if possible.
+ // This avoids more expensive work later.
+ if (CanonKind == CanonicalizationKind::Functional) {
+ llvm::FoldingSetNodeID ID;
+ CanonKind = Arg.getAsExpr()->Profile(ID, *this, CanonKind);
+ }
+ AnyNonCanonical |= CanonKind != CanonicalizationKind::Structural;
+ return TemplateArgument(Arg.getAsExpr(), CanonKind, Arg.getIsDefaulted());
}
+ case TemplateArgument::Declaration:
+ return TemplateArgument(
+ cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl()),
+ getCanonicalType(Arg.getParamTypeForDecl(), Kind, AnyNonCanonical),
+ Arg.getIsDefaulted());
+
case TemplateArgument::NullPtr:
- return TemplateArgument(getCanonicalType(Arg.getNullPtrType()),
- /*isNullPtr*/ true, Arg.getIsDefaulted());
+ return TemplateArgument(
+ getCanonicalType(Arg.getNullPtrType(), Kind, AnyNonCanonical),
+ /*isNullPtr=*/true, Arg.getIsDefaulted());
case TemplateArgument::Template:
- return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()),
+ return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate(),
+ /*IgnoreDeduced=*/false,
+ Kind, AnyNonCanonical),
Arg.getIsDefaulted());
case TemplateArgument::TemplateExpansion:
return TemplateArgument(
- getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern()),
+ getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern(),
+ /*IgnoreDeduced=*/false, Kind,
+ AnyNonCanonical),
Arg.getNumTemplateExpansions(), Arg.getIsDefaulted());
case TemplateArgument::Integral:
- return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType()));
+ return TemplateArgument(
+ Arg, getCanonicalType(Arg.getIntegralType(), Kind, AnyNonCanonical));
case TemplateArgument::StructuralValue:
- return TemplateArgument(*this,
- getCanonicalType(Arg.getStructuralValueType()),
- Arg.getAsStructuralValue(), Arg.getIsDefaulted());
+ return TemplateArgument(
+ *this,
+ getCanonicalType(Arg.getStructuralValueType(), Kind, AnyNonCanonical),
+ Arg.getAsStructuralValue(), Arg.getIsDefaulted());
case TemplateArgument::Type:
- return TemplateArgument(getCanonicalType(Arg.getAsType()),
- /*isNullPtr*/ false, Arg.getIsDefaulted());
+ return TemplateArgument(
+ getCanonicalType(Arg.getAsType(), Kind, AnyNonCanonical),
+ /*isNullPtr=*/false, Arg.getIsDefaulted());
case TemplateArgument::Pack: {
- bool AnyNonCanonArgs = false;
+ bool AnyChanged = false;
auto CanonArgs = ::getCanonicalTemplateArguments(
- *this, Arg.pack_elements(), AnyNonCanonArgs);
- if (!AnyNonCanonArgs)
+ *this, Arg.pack_elements(), AnyChanged, Kind, AnyNonCanonical);
+ if (!AnyChanged)
return Arg;
auto NewArg = TemplateArgument::CreatePackCopy(
const_cast<ASTContext &>(*this), CanonArgs);
@@ -7978,8 +8117,8 @@ bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1,
case TemplateArgument::Expression: {
llvm::FoldingSetNodeID ID1, ID2;
- Arg1.getAsExpr()->Profile(ID1, *this, /*Canonical=*/true);
- Arg2.getAsExpr()->Profile(ID2, *this, /*Canonical=*/true);
+ Arg1.getAsExpr()->Profile(ID1, *this, CanonicalizationKind::Structural);
+ Arg2.getAsExpr()->Profile(ID2, *this, CanonicalizationKind::Structural);
return ID1 == ID2;
}
@@ -8090,7 +8229,11 @@ QualType ASTContext::getArrayDecayedType(QualType Ty) const {
const ArrayType *PrettyArrayType = getAsArrayType(Ty);
assert(PrettyArrayType && "Not an array type!");
- QualType PtrTy = getPointerType(PrettyArrayType->getElementType());
+ // Add outer qualifiers to element type.
+ QualType ElementType = getQualifiedType(PrettyArrayType->getElementType(),
+ Ty.getLocalQualifiers());
+
+ QualType PtrTy = getPointerType(ElementType);
// int x[restrict 4] -> int *restrict
QualType Result = getQualifiedType(PtrTy,
@@ -13858,14 +14001,555 @@ unsigned ASTContext::getTargetAddressSpace(LangAS AS) const {
return getTargetInfo().getTargetAddressSpace(AS);
}
-bool ASTContext::hasSameExpr(const Expr *X, const Expr *Y) const {
+NestedNameSpecifier
+ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier Qualifier,
+ CanonicalizationKind CanonKind,
+ bool &AnyNonCanonical) const {
+ if (CanonKind == CanonicalizationKind::Functional &&
+ Qualifier.getKind() == NestedNameSpecifier::Kind::Type)
+ return NestedNameSpecifier(
+ getCanonicalType(QualType(Qualifier.getAsType(), 0),
+ CanonicalizationKind::Functional, AnyNonCanonical)
+ .getTypePtr());
+ return Qualifier.getCanonical();
+}
+
+QualType ASTContext::getCanonicalType(QualType QT,
+ CanonicalizationKind Kind) const {
+ if (Kind == CanonicalizationKind::Structural)
+ return QT.getCanonicalType();
+ assert(Kind == CanonicalizationKind::Functional);
+
+ // A structural canonical type is functionally equivalent to itself.
+ if (QT.isCanonical())
+ return QT;
+
+ auto [T, Qualifiers] = QT.split();
+
+ // A non-instantiation-dependent type doesn't refer to any template
+ // parameters. The template parameters themselves never fail substitution, so
+ // they are functionally equivalent to their structural canonical type.
+ // A template type parameter substitution can't refer to any unsubstituted
+ // template parameters syntactically.
+ if (!T->isInstantiationDependentType() ||
+ isa<TemplateTypeParmType, SubstTemplateTypeParmType>(T))
+ return QT.getCanonicalType();
+
+ SplitQualType ST;
+ auto It = FunctionallyEquivalentTypeCache.find(T);
+ if (It != FunctionallyEquivalentTypeCache.end())
+ ST = It->second.split();
+ else {
+ ST = buildFunctionallyEquivalentCanonicalType(T);
+ QualType R = getQualifiedType(ST);
+
+ assert(hasSameType(QualType(T, 0), R));
+
+ auto [_, Inserted] = FunctionallyEquivalentTypeCache.try_emplace(T, R);
+ assert(Inserted && "Unexpected cache entry for type");
+ }
+ ST.Quals += Qualifiers;
+ // Remove redundant top level qualifiers.
+ ST.Quals -= ST.Ty->getCanonicalTypeInternal().getLocalQualifiers();
+ QualType R = getQualifiedType(ST);
+ assert(hasSameType(QT, R));
+ return R;
+}
+
+SplitQualType
+ASTContext::buildFunctionallyEquivalentCanonicalType(const Type *T) const {
+ assert(!T->isCanonicalUnqualified());
+ assert(T->isInstantiationDependentType());
+
+ // If none of the inputs became non-canonical, just return the canonical type.
+ // It's not helpful for the applications of this transform to track whether
+ // the inputs changed, because they will mostly refer to template parameters
+ // that haven't been canonicalized.
+ bool AnyNonCanonical = false;
+ switch (T->getTypeClass()) {
+ case Type::Builtin:
+ llvm_unreachable(
+ "always canonical types should have been handled by this point");
+ case Type::PredefinedSugar:
+ llvm_unreachable("never-instantation-dependent types should have been "
+ "handled by this point");
+ case Type::SubstTemplateTypeParm:
+ case Type::TemplateTypeParm:
+ llvm_unreachable("template parameters and their substitutions should have "
+ "been handled by this point");
+ case Type::Complex:
+ case Type::FunctionNoProto:
+ case Type::Adjusted:
+ case Type::ObjCObject:
+ case Type::ObjCInterface:
+ case Type::ObjCObjectPointer:
+ case Type::DependentVector:
+ case Type::Vector:
+ case Type::ArrayParameter:
+ case Type::BTFTagAttributed:
+ case Type::BitInt:
+ case Type::CountAttributed:
+ case Type::HLSLAttributedResource:
+ case Type::HLSLInlineSpirv:
+ case Type::ObjCTypeParam:
+ case Type::Pipe:
+ case Type::TypeOf:
+ case Type::DependentBitInt:
+ case Type::OverflowBehavior:
+ case Type::SubstBuiltinTemplatePack:
+ // FIXME: These are all unimplemented.
+ // Just return the structural canonical type, which is the easy thing to do.
+ break;
+ case Type::Atomic: {
+ const auto *TT = cast<AtomicType>(T);
+ QualType Value = getCanonicalType(
+ TT->getValueType(), CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getAtomicType(Value).split();
+ }
+ case Type::Auto: {
+ const auto *TT = cast<AutoType>(T);
+ SmallVector<TemplateArgument, 4> Args(TT->getTypeConstraintArguments());
+ canonicalizeTemplateArguments(Args, CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ QualType DeducedAsType = TT->getDeducedType();
+ TemplateDecl *TD = TT->getTypeConstraintConcept();
+ return getAutoType(
+ TT->getDeducedKind(),
+ DeducedAsType.isNull() ? QualType()
+ : DeducedAsType.getCanonicalType(),
+ TT->getKeyword(),
+ TD ? cast<TemplateDecl>(TD->getCanonicalDecl()) : nullptr, Args)
+ .split();
+ }
+ case Type::FunctionProto: {
+ const auto *TT = cast<FunctionProtoType>(T);
+ FunctionProtoType::ExtProtoInfo EPI = TT->getExtProtoInfo();
+ EPI.HasTrailingReturn = false;
+ SmallVector<QualType, 8> ExceptionTypeStorage;
+ EPI.ExceptionSpec = ::getCanonicalExceptionSpec(
+ *this, EPI.ExceptionSpec, ExceptionTypeStorage,
+ CanonicalizationKind::Functional, AnyNonCanonical);
+ QualType RT = getCanonicalFunctionResultType(
+ TT->getReturnType(), CanonicalizationKind::Functional, AnyNonCanonical);
+ SmallVector<QualType, 8> PTs(TT->getParamTypes().size());
+ llvm::transform(TT->getParamTypes(), PTs.begin(), [&](QualType PT) {
+ return getCanonicalParamType(PT, CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ });
+ if (!AnyNonCanonical)
+ break;
+ return getFunctionType(RT, PTs, EPI).split();
+ }
+ case Type::Pointer: {
+ const auto *TT = cast<PointerType>(T);
+ QualType Pointee =
+ getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getPointerType(Pointee).split();
+ }
+ case Type::LValueReference: {
+ const auto *TT = cast<LValueReferenceType>(T);
+ QualType Pointee =
+ getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getLValueReferenceType(Pointee).split();
+ }
+ case Type::RValueReference: {
+ const auto *TT = cast<RValueReferenceType>(T);
+ QualType Pointee =
+ getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getRValueReferenceType(Pointee).split();
+ }
+ case Type::Enum:
+ case Type::Record:
+ case Type::InjectedClassName: {
+ const auto *TT = cast<TagType>(T);
+ NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+ TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getTagType(ElaboratedTypeKeyword::None, Qualifier,
+ TT->getDecl()->getCanonicalDecl(),
+ /*OwnsTag=*/false)
+ .split();
+ }
+ case Type::UnresolvedUsing: {
+ const auto *TT = cast<UnresolvedUsingType>(T);
+ NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+ TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getUnresolvedUsingType(ElaboratedTypeKeyword::None, Qualifier,
+ TT->getDecl()->getCanonicalDecl())
+ .split();
+ }
+ case Type::Using: {
+ const auto *TT = cast<UsingType>(T);
+ NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+ TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getUsingType(ElaboratedTypeKeyword::None, Qualifier,
+ TT->getDecl()->getCanonicalDecl(),
+ TT->getCanonicalTypeInternal())
+ .split();
+ }
+ case Type::Typedef: {
+ const auto *TT = cast<TypedefType>(T);
+ NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+ TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getTypedefType(ElaboratedTypeKeyword::None, Qualifier,
+ TT->getDecl()->getCanonicalDecl())
+ .split();
+ }
+ case Type::Attributed:
+ return getCanonicalType(cast<AttributedType>(T)->desugar(),
+ CanonicalizationKind::Functional, AnyNonCanonical)
+ .split();
+ case Type::MacroQualified:
+ return getCanonicalType(cast<MacroQualifiedType>(T)->desugar(),
+ CanonicalizationKind::Functional, AnyNonCanonical)
+ .split();
+ case Type::Paren:
+ return getCanonicalType(cast<ParenType>(T)->desugar(),
+ CanonicalizationKind::Functional, AnyNonCanonical)
+ .split();
+ case Type::PackExpansion: {
+ const auto *TT = cast<PackExpansionType>(T);
+ QualType Pattern = getCanonicalType(
+ TT->getPattern(), CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getPackExpansionType(Pattern, TT->getNumExpansions(),
+ /*ExpectPackInType=*/false)
+ .split();
+ }
+ case Type::PackIndexing: {
+ const auto *TT = cast<PackIndexingType>(T);
+
+ // This type holds on to instantiation-dependence.
+ assert(!TT->isSugared());
+
+ QualType Pattern = getCanonicalType(
+ TT->getPattern(), CanonicalizationKind::Functional, AnyNonCanonical);
+ SmallVector<QualType, 8> Expansions(TT->getExpansions().size());
+ llvm::transform(TT->getExpansions(), Expansions.begin(), [&](QualType E) {
+ return getCanonicalType(E, CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ });
+ if (!AnyNonCanonical)
+ break;
+ return getPackIndexingType(Pattern, TT->getIndexExpr(),
+ TT->isFullySubstituted(), Expansions,
+ TT->getSelectedIndex())
+ .split();
+ }
+ case Type::UnaryTransform:
+ // FIXME: Unimplemented. These appear to have broken uniquing.
+ break;
+ case Type::MemberPointer: {
+ const auto *TT = cast<MemberPointerType>(T);
+ const auto *Cls = TT->getMostRecentCXXRecordDecl();
+ NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+ TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+ // If the qualifier became canonical and we have a class (ie it is
+ // non-dependent), then we can drop the qualifier entirely.
+ if (Cls && !AnyNonCanonical)
+ Qualifier = std::nullopt;
+ QualType Pointee =
+ getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getMemberPointerType(Pointee, Qualifier, Cls).split();
+ }
+ case Type::BlockPointer: {
+ const auto *TT = cast<BlockPointerType>(T);
+ QualType Pointee =
+ getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getBlockPointerType(Pointee).split();
+ }
+ // Arrays canonicalize by having their qualifiers pushed outside.
+ case Type::IncompleteArray: {
+ const auto *TT = cast<IncompleteArrayType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ auto [ETy, EQuals] = ElementType.split();
+ return SplitQualType(getIncompleteArrayType(QualType(ETy, 0),
+ TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers())
+ .getTypePtr(),
+ EQuals);
+ }
+ case Type::ConstantArray: {
+ const auto *TT = cast<ConstantArrayType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ auto [ETy, EQuals] = ElementType.split();
+ return SplitQualType(getConstantArrayType(QualType(ETy, 0), TT->getSize(),
+ TT->getSizeExpr(),
+ TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers())
+ .getTypePtr(),
+ EQuals);
+ }
+ case Type::VariableArray: {
+ const auto *TT = cast<VariableArrayType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ auto [ETy, EQuals] = ElementType.split();
+ return SplitQualType(getVariableArrayType(QualType(ETy, 0),
+ TT->getSizeExpr(),
+ TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers())
+ .getTypePtr(),
+ EQuals);
+ }
+ case Type::DependentSizedArray: {
+ const auto *TT = cast<DependentSizedArrayType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ auto [ETy, EQuals] = ElementType.split();
+ return SplitQualType(
+ getDependentSizedArrayType(QualType(ETy, 0), TT->getSizeExpr(),
+ TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers())
+ .getTypePtr(),
+ EQuals);
+ }
+ case Type::ExtVector: {
+ const auto *TT = cast<ExtVectorType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getExtVectorType(ElementType, TT->getNumElements()).split();
+ }
+ case Type::DependentSizedExtVector: {
+ const auto *TT = cast<DependentSizedExtVectorType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentSizedExtVectorType(ElementType, TT->getSizeExpr(),
+ TT->getAttributeLoc())
+ .split();
+ }
+ case Type::ConstantMatrix: {
+ const auto *TT = cast<ConstantMatrixType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getConstantMatrixType(ElementType, TT->getNumRows(),
+ TT->getNumColumns())
+ .split();
+ }
+ case Type::DependentSizedMatrix: {
+ const auto *TT = cast<DependentSizedMatrixType>(T);
+ QualType ElementType =
+ getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentSizedMatrixType(ElementType, TT->getRowExpr(),
+ TT->getColumnExpr(),
+ TT->getAttributeLoc())
+ .split();
+ }
+ case Type::DependentAddressSpace: {
+ const auto *TT = cast<DependentAddressSpaceType>(T);
+ QualType Pointee =
+ getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentAddressSpaceType(Pointee, TT->getAddrSpaceExpr(),
+ TT->getAttributeLoc())
+ .split();
+ }
+ case Type::SubstTemplateTypeParmPack: {
+ const auto *TT = cast<SubstTemplateTypeParmPackType>(T);
+ TemplateArgument ArgPack = getCanonicalTemplateArgument(
+ TT->getArgumentPack(), CanonicalizationKind::Functional,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getSubstTemplateTypeParmPackType(
+ TT->getAssociatedDecl()->getCanonicalDecl(), TT->getIndex(),
+ TT->getFinal(), ArgPack)
+ .split();
+ }
+ case Type::DependentName: {
+ const auto *TT = cast<DependentNameType>(T);
+ NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+ TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentNameType(
+ ::getCanonicalElaboratedTypeKeyword(TT->getKeyword()), Qualifier,
+ TT->getIdentifier())
+ .split();
+ }
+ case Type::TemplateSpecialization: {
+ const auto *TT = cast<TemplateSpecializationType>(T);
+ TemplateName TN = getCanonicalTemplateName(
+ TT->getTemplateName(), /*IgnoreDeduced=*/true,
+ CanonicalizationKind::Functional, AnyNonCanonical);
+
+ // For a type alias, additionally we want to keep if there are unused
+ // instantiation-dependent template arguments.
+ if (!AnyNonCanonical && TT->isTypeAlias()) {
+ const TemplateDecl *TD = TT->getTemplateName().getAsTemplateDecl();
+ auto As = TT->template_arguments();
+ for (const NamedDecl *PD : TD->getTemplateParameters()->asArray()) {
+ if (As.empty())
+ break;
+ auto CurAs = As;
+ // If this is a parameter pack, a use of this parameter means all of the
+ // remaining template arguments are used.
+ if (!PD->isTemplateParameterPack())
+ CurAs = CurAs.take_front(1);
+ if (!PD->isReferenced() &&
+ llvm::any_of(CurAs, [](const TemplateArgument &A) {
+ return A.isInstantiationDependent();
+ })) {
+ AnyNonCanonical = true;
+ break;
+ }
+ As = As.drop_front(CurAs.size());
+ }
+ if (!AnyNonCanonical)
+ return getCanonicalType(TT->getAliasedType(),
+ CanonicalizationKind::Functional,
+ AnyNonCanonical)
+ .split();
+ }
+ // FIXME: We can't avoid rebuilding if nothing changed, because we can't
+ // rely on this TST to have been uniqued.
+ // FIXME: This should contain default arguments as well, but we don't have a
+ // cheap way to get those here.
+ bool AnyChanged = false;
+ auto As = ::getCanonicalTemplateArguments(
+ *this, TT->template_arguments(), AnyChanged,
+ CanonicalizationKind::Functional, AnyNonCanonical);
+ bool NameIsDependent = TN.isDependent();
+ if (!AnyNonCanonical) {
+ // If any template arguments are instantiation-dependent-only expressions
+ // and the name is not dependent, then we can't use the canonical template
+ // specialization type, because their corresponding template arguments
+ // would not be expressions anymore, and would lose any references to
+ // template parameters.
+ if (NameIsDependent || llvm::none_of(As, [](const TemplateArgument &A) {
+ if (A.getKind() != TemplateArgument::Expression &&
+ !(A.getKind() == TemplateArgument::Pack && A.pack_size() > 0 &&
+ A.pack_elements()[0].getKind() ==
+ TemplateArgument::Expression))
+ return false;
+ TemplateArgumentDependence Dep = A.getDependence();
+ return (Dep & TemplateArgumentDependence::Instantiation) &&
+ !(Dep & TemplateArgumentDependence::Dependent);
+ }))
+ break;
+ }
+ return getTemplateSpecializationType(
+ NameIsDependent
+ ? ::getCanonicalElaboratedTypeKeyword(TT->getKeyword())
+ : ElaboratedTypeKeyword::None,
+ TN, As,
+ /*CanonicalArgs=*/ArrayRef<TemplateArgument>(),
+ TT->desugar().getCanonicalType(),
+ /*Unique=*/true)
+ .split();
+ }
+ case Type::DeducedTemplateSpecialization: {
+ const auto *TT = cast<DeducedTemplateSpecializationType>(T);
+ TemplateName TN =
+ getCanonicalTemplateName(TT->getTemplateName(), /*IgnoreDeduced=*/true,
+ CanonicalizationKind::Functional);
+ if (!AnyNonCanonical)
+ break;
+ return getDeducedTemplateSpecializationType(
+ TT->getDeducedKind(), TT->desugar().getCanonicalType(),
+ ElaboratedTypeKeyword::None, TN)
+ .split();
+ }
+ case Type::Decayed: {
+ const auto *TT = cast<DecayedType>(T);
+ QualType OriginalType =
+ getCanonicalType(TT->getOriginalType(),
+ CanonicalizationKind::Functional, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDecayedType(OriginalType, TT->getDecayedType().getCanonicalType())
+ .split();
+ }
+ case Type::Decltype: {
+ const auto *TT = cast<DecltypeType>(T);
+ QualType UnderlyingType = TT->getUnderlyingType();
+ if (!UnderlyingType.isNull()) {
+ AnyNonCanonical = true;
+ UnderlyingType = UnderlyingType.getCanonicalType();
+ }
+ auto ExprCanonKind = std::max<CanonicalizationKindOrNone>(
+ TT->getExprCanonicalizationKind(), CanonicalizationKind::Functional);
+ return getDecltypeType(TT->getUnderlyingExpr(), ExprCanonKind,
+ UnderlyingType)
+ .split();
+ }
+ case Type::TypeOfExpr: {
+ const auto *TT = cast<TypeOfExprType>(T);
+ // FIXME: Can't be implemented, as sugar TypeOfExprTypes are not uniqued,
+ // and they can't be canonicalzied on the expression CanonKind.
+ if (!TT->isSugared())
+ break;
+ AnyNonCanonical = true;
+ return SplitQualType(T, Qualifiers());
+ }
+ }
+ return T->getCanonicalTypeInternal().split();
+}
+
+bool ASTContext::hasSameExpr(const Expr *X, const Expr *Y,
+ CanonicalizationKindOrNone CanonKind) const {
if (X == Y)
return true;
if (!X || !Y)
return false;
llvm::FoldingSetNodeID IDX, IDY;
- X->Profile(IDX, *this, /*Canonical=*/true);
- Y->Profile(IDY, *this, /*Canonical=*/true);
+ X->Profile(IDX, *this, CanonKind);
+ Y->Profile(IDY, *this, CanonKind);
return IDX == IDY;
}
@@ -14242,6 +14926,29 @@ ASTContext::mergeExceptionSpecs(FunctionProtoType::ExceptionSpecInfo ESI1,
llvm_unreachable("invalid ExceptionSpecificationType");
}
+static CanonicalizationKindOrNone
+getCommonExprCanonicalizationKind(const ASTContext &Ctx, const Expr *EX,
+ CanonicalizationKindOrNone KX, const Expr *EY,
+ CanonicalizationKindOrNone KY) {
+ CanonicalizationKindOrNone MaxExprCanonKind = std::max(KX, KY);
+ CanonicalizationKindOrNone ExprCanonKind = CanonicalizationKind::Structural;
+ // FIXME: Also try FunctionallyEquivalent canonicalization.
+ if (!MaxExprCanonKind && Ctx.hasSameExpr(EX, EY,
+ /*CanonKind=*/std::nullopt))
+ ExprCanonKind = std::nullopt;
+ return ExprCanonKind;
+}
+
+static std::tuple<bool, CanonicalizationKindOrNone>
+getCommonExprCanonicalizationKindChecked(const ASTContext &Ctx, const Expr *EX,
+ CanonicalizationKindOrNone KX,
+ const Expr *EY,
+ CanonicalizationKindOrNone KY) {
+ if (!Ctx.hasSameExpr(EX, EY, CanonicalizationKind::Structural))
+ return {true, std::nullopt};
+ return {false, ::getCommonExprCanonicalizationKind(Ctx, EX, KX, EY, KY)};
+}
+
static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X,
Qualifiers &QX, const Type *Y,
Qualifiers &QY) {
@@ -14511,9 +15218,17 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X,
[[maybe_unused]] const auto *DY = cast<DecltypeType>(Y);
assert(DX->isDependentType());
assert(DY->isDependentType());
- assert(Ctx.hasSameExpr(DX->getUnderlyingExpr(), DY->getUnderlyingExpr()));
- // As Decltype is not uniqued, building a common type would be wasteful.
- return QualType(DX, 0);
+ assert(DX->getUnderlyingType().isNull());
+ assert(DY->getUnderlyingType().isNull());
+
+ Expr *E = DX->getUnderlyingExpr();
+ assert(Ctx.hasSameExpr(E, DY->getUnderlyingExpr()));
+
+ CanonicalizationKindOrNone ExprCanonKind =
+ ::getCommonExprCanonicalizationKind(
+ Ctx, E, DX->getExprCanonicalizationKind(), DY->getUnderlyingExpr(),
+ DY->getExprCanonicalizationKind());
+ return Ctx.getDecltypeType(E, ExprCanonKind, QualType());
}
case Type::PackIndexing: {
const auto *DX = cast<PackIndexingType>(X);
@@ -14689,9 +15404,25 @@ static QualType getCommonSugarTypeNode(const ASTContext &Ctx, const Type *X,
Ctx.getQualifiedType(Underlying), AX->getKeyword(),
CD, As);
}
+ case Type::Decltype: {
+ const auto *DX = cast<DecltypeType>(X);
+ [[maybe_unused]] const auto *DY = cast<DecltypeType>(Y);
+ assert(!DX->isDependentType());
+ assert(!DY->isDependentType());
+ assert(!DX->getUnderlyingType().isNull());
+ assert(!DY->getUnderlyingType().isNull());
+
+ Expr *E = DX->getUnderlyingExpr();
+ auto [NotEquivalent, ExprCanonKind] =
+ ::getCommonExprCanonicalizationKindChecked(
+ Ctx, E, DX->getExprCanonicalizationKind(), DY->getUnderlyingExpr(),
+ DY->getExprCanonicalizationKind());
+ if (NotEquivalent)
+ return QualType();
+ return Ctx.getDecltypeType(E, ExprCanonKind,
+ Ctx.getQualifiedType(Underlying));
+ }
case Type::PackIndexing:
- case Type::Decltype:
- return QualType();
case Type::DeducedTemplateSpecialization:
// FIXME: Try to merge these.
return QualType();
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index b8023cb6fa10f..edb19e9426c61 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -126,7 +126,7 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT,
ShouldAKA = true;
QT = Context.getTemplateSpecializationType(
TST->getKeyword(), TST->getTemplateName(), Args,
- /*CanonicalArgs=*/{}, QT);
+ /*CanonicalArgs=*/{}, QT.getCanonicalType());
}
break;
}
@@ -1534,8 +1534,8 @@ class TemplateDiff {
return false;
llvm::FoldingSetNodeID FromID, ToID;
- FromExpr->Profile(FromID, Context, true);
- ToExpr->Profile(ToID, Context, true);
+ FromExpr->Profile(FromID, Context, CanonicalizationKind::Structural);
+ ToExpr->Profile(ToID, Context, CanonicalizationKind::Structural);
return FromID == ToID;
}
@@ -2149,15 +2149,21 @@ class TemplateDiff {
return;
}
+ TemplateDecl *FromOrigTD = FromOrigTST->getTemplateName().getAsTemplateDecl(
+ /*IgnoreDeduced=*/true);
+ TemplateDecl *ToOrigTD = ToOrigTST->getTemplateName().getAsTemplateDecl(
+ /*IgnoreDeduced=*/true);
+ // If either side does not have a template declaration, then there are no
+ // template parameters, and nothing further to diff.
+ if (!FromOrigTD || !ToOrigTD)
+ return;
+
FromQual -= QualType(FromOrigTST, 0).getQualifiers();
ToQual -= QualType(ToOrigTST, 0).getQualifiers();
// Same base template, but different arguments.
- Tree.SetTemplateDiff(
- FromOrigTST->getTemplateName().getAsTemplateDecl(
- /*IgnoreDeduced=*/true),
- ToOrigTST->getTemplateName().getAsTemplateDecl(/*IgnoreDeduced=*/true),
- FromQual, ToQual, false /*FromDefault*/, false /*ToDefault*/);
+ Tree.SetTemplateDiff(FromOrigTD, ToOrigTD, FromQual, ToQual,
+ false /*FromDefault*/, false /*ToDefault*/);
DiffTemplate(FromOrigTST, ToOrigTST);
}
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 41ba98c53247d..2cb51c2625f91 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -913,7 +913,7 @@ ASTNodeImporter::import(const TemplateArgument &From) {
case TemplateArgument::Expression:
if (ExpectedExpr ToExpr = import(From.getAsExpr()))
- return TemplateArgument(*ToExpr, From.isCanonicalExpr(),
+ return TemplateArgument(*ToExpr, From.getExprCanonKind(),
From.getIsDefaulted());
else
return ToExpr.takeError();
@@ -1710,7 +1710,7 @@ ExpectedType ASTNodeImporter::VisitDecltypeType(const DecltypeType *T) {
return ToUnderlyingTypeOrErr.takeError();
return Importer.getToContext().getDecltypeType(
- *ToExprOrErr, *ToUnderlyingTypeOrErr);
+ *ToExprOrErr, T->getExprCanonicalizationKind(), *ToUnderlyingTypeOrErr);
}
ExpectedType
@@ -6491,7 +6491,6 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl(
if (GetImportedOrCreateDecl<ClassTemplatePartialSpecializationDecl>(
D2, D, Importer.getToContext(), D->getTagKind(), DC, *BeginLocOrErr,
*IdLocOrErr, ToTPList, ClassTemplate, ArrayRef(TemplateArgs),
- /*CanonInjectedTST=*/CanQualType(),
cast_or_null<ClassTemplatePartialSpecializationDecl>(PrevDecl)))
return D2;
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 99d02fdc99e92..1b9c09bda9035 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -139,16 +139,17 @@ void TemplateParameterList::Profile(llvm::FoldingSetNodeID &ID,
const Expr *RC = getRequiresClause();
ID.AddBoolean(RC != nullptr);
if (RC)
- RC->Profile(ID, C, /*Canonical=*/true);
+ RC->Profile(ID, C, CanonicalizationKind::Functional);
ID.AddInteger(size());
for (NamedDecl *D : *this) {
if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) {
ID.AddInteger(0);
ID.AddBoolean(NTTP->isParameterPack());
- NTTP->getType().getCanonicalType().Profile(ID);
+ C.getCanonicalType(NTTP->getType(), CanonicalizationKind::Functional)
+ .Profile(ID);
ID.AddBoolean(NTTP->hasPlaceholderTypeConstraint());
if (const Expr *E = NTTP->getPlaceholderTypeConstraint())
- E->Profile(ID, C, /*Canonical=*/true);
+ E->Profile(ID, C, CanonicalizationKind::Functional);
continue;
}
if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) {
@@ -156,8 +157,8 @@ void TemplateParameterList::Profile(llvm::FoldingSetNodeID &ID,
ID.AddBoolean(TTP->isParameterPack());
ID.AddBoolean(TTP->hasTypeConstraint());
if (const TypeConstraint *TC = TTP->getTypeConstraint())
- TC->getImmediatelyDeclaredConstraint()->Profile(ID, C,
- /*Canonical=*/true);
+ TC->getImmediatelyDeclaredConstraint()->Profile(
+ ID, C, CanonicalizationKind::Functional);
continue;
}
const auto *TTP = cast<TemplateTemplateParmDecl>(D);
@@ -1165,15 +1166,13 @@ ClassTemplatePartialSpecializationDecl::ClassTemplatePartialSpecializationDecl(
ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, TemplateParameterList *Params,
ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args,
- CanQualType CanonInjectedTST,
ClassTemplatePartialSpecializationDecl *PrevDecl)
: ClassTemplateSpecializationDecl(
Context, ClassTemplatePartialSpecialization, TK, DC, StartLoc, IdLoc,
// Tracking StrictPackMatch for Partial
// Specializations is not needed.
SpecializedTemplate, Args, /*StrictPackMatch=*/false, PrevDecl),
- TemplateParams(Params), InstantiatedFromMember(nullptr, false),
- CanonInjectedTST(CanonInjectedTST) {
+ TemplateParams(Params), InstantiatedFromMember(nullptr, false) {
if (AdoptTemplateParameterList(Params, this))
setInvalidDecl();
}
@@ -1183,11 +1182,10 @@ ClassTemplatePartialSpecializationDecl::Create(
ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, TemplateParameterList *Params,
ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args,
- CanQualType CanonInjectedTST,
ClassTemplatePartialSpecializationDecl *PrevDecl) {
auto *Result = new (Context, DC) ClassTemplatePartialSpecializationDecl(
Context, TK, DC, StartLoc, IdLoc, Params, SpecializedTemplate, Args,
- CanonInjectedTST, PrevDecl);
+ PrevDecl);
Result->setSpecializationKind(TSK_ExplicitSpecialization);
return Result;
}
@@ -1202,11 +1200,13 @@ CanQualType
ClassTemplatePartialSpecializationDecl::getCanonicalInjectedSpecializationType(
const ASTContext &Ctx) const {
if (CanonInjectedTST.isNull()) {
+ SmallVector<TemplateArgument, 4> CanonicalArgs(getTemplateArgs().asArray());
+ Ctx.canonicalizeTemplateArguments(CanonicalArgs);
CanonInjectedTST =
CanQualType::CreateUnsafe(Ctx.getCanonicalTemplateSpecializationType(
ElaboratedTypeKeyword::None,
TemplateName(getSpecializedTemplate()->getCanonicalDecl()),
- getTemplateArgs().asArray()));
+ CanonicalArgs));
}
return CanonInjectedTST;
}
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index f58faa03bfa8c..96b6ffe5ca250 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -544,8 +544,9 @@ class CXXNameMangler {
void manglePrefix(NestedNameSpecifier Qualifier);
void manglePrefix(const DeclContext *DC, bool NoFunction=false);
void manglePrefix(QualType type);
+ void mangleNameWithPrefix(GlobalDecl GD, NestedNameSpecifier Qualifier);
void mangleTemplatePrefix(GlobalDecl GD, bool NoFunction=false);
- void mangleTemplatePrefix(TemplateName Template);
+ [[nodiscard]] bool mangleTemplatePrefix(TemplateName Template, bool InPrefix);
const NamedDecl *getClosurePrefix(const Decl *ND);
void mangleClosurePrefix(const NamedDecl *ND, bool NoFunction = false);
bool mangleUnresolvedTypeOrSimpleId(QualType DestroyedType,
@@ -559,7 +560,6 @@ class CXXNameMangler {
// Declare manglers for every type class.
#define ABSTRACT_TYPE(CLASS, PARENT)
-#define NON_CANONICAL_TYPE(CLASS, PARENT)
#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T);
#include "clang/AST/TypeNodes.inc"
@@ -1206,7 +1206,7 @@ void CXXNameMangler::mangleUnscopedTemplateName(
assert(!AdditionalAbiTags &&
"template template param cannot have abi tags");
mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
- } else if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND)) {
+ } else if (isa<BuiltinTemplateDecl, ConceptDecl, TypeAliasTemplateDecl>(ND)) {
mangleUnscopedName(GD, DC, AdditionalAbiTags);
} else {
mangleUnscopedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
@@ -1320,7 +1320,7 @@ void CXXNameMangler::mangleCallOffset(int64_t NonVirtual, int64_t Virtual) {
void CXXNameMangler::manglePrefix(QualType type) {
if (const auto *TST = type->getAs<TemplateSpecializationType>()) {
if (!mangleSubstitution(QualType(TST, 0))) {
- mangleTemplatePrefix(TST->getTemplateName());
+ (void)mangleTemplatePrefix(TST->getTemplateName(), /*InPrefix=*/true);
// FIXME: GCC does not appear to mangle the template arguments when
// the template in question is a dependent template name. Should we
@@ -2259,39 +2259,102 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) {
addSubstitution(ND);
}
-void CXXNameMangler::mangleTemplatePrefix(TemplateName Template) {
+bool CXXNameMangler::mangleTemplatePrefix(TemplateName Template,
+ bool InPrefix) {
// <template-prefix> ::= <prefix> <template unqualified-name>
// ::= <template-param>
// ::= <substitution>
- if (TemplateDecl *TD = Template.getAsTemplateDecl())
- return mangleTemplatePrefix(TD);
+ switch (Template.getKind()) {
+ case clang::TemplateName::Template:
+ case clang::TemplateName::UsingTemplate: {
+ const TemplateDecl *TD = Template.getAsTemplateDecl();
+ const DeclContext *DC = Context.getEffectiveDeclContext(TD);
+
+ if (DC->isTranslationUnit() || isStdNamespace(DC)) {
+ mangleUnscopedTemplateName(TD, DC, nullptr);
+ return false;
+ }
+ if (!InPrefix)
+ Out << 'N';
+ mangleTemplatePrefix(TD);
+ return true;
+ }
+ case clang::TemplateName::QualifiedTemplate: {
+ const QualifiedTemplateName *S = Template.getAsQualifiedTemplateName();
- DependentTemplateName *Dependent = Template.getAsDependentTemplateName();
- assert(Dependent && "unexpected template name kind");
+ if (!InPrefix)
+ Out << 'N';
+ manglePrefix(S->getQualifier());
- // Clang 11 and before mangled the substitution for a dependent template name
- // after already having emitted (a substitution for) the prefix.
- bool Clang11Compat = isCompatibleWith(LangOptions::ClangABI::Ver11);
- if (!Clang11Compat && mangleSubstitution(Template))
- return;
+ TemplateName Underlying = S->getUnderlyingTemplate();
+ const NamedDecl *ND;
+ switch (Underlying.getKind()) {
+ case TemplateName::Template:
+ ND = Underlying.getAsTemplateDecl();
+ break;
+ case TemplateName::UsingTemplate:
+ ND = Underlying.getAsUsingShadowDecl();
+ break;
+ default:
+ llvm_unreachable(
+ "unexpected underlying template name kind for qualified template");
+ }
+ mangleTemplatePrefix(ND);
+ return true;
+ break;
+ }
+ case clang::TemplateName::DependentTemplate: {
+ DependentTemplateName *Dependent = Template.getAsDependentTemplateName();
+ assert(Dependent && "unexpected template name kind");
- manglePrefix(Dependent->getQualifier());
+ if (!InPrefix)
+ Out << 'N';
- if (Clang11Compat && mangleSubstitution(Template))
- return;
+ // Clang 11 and before mangled the substitution for a dependent template
+ // name after already having emitted (a substitution for) the prefix.
+ bool Clang11Compat = isCompatibleWith(LangOptions::ClangABI::Ver11);
+ if (!Clang11Compat && mangleSubstitution(Template))
+ return true;
- if (IdentifierOrOverloadedOperator Name = Dependent->getName();
- const IdentifierInfo *Id = Name.getIdentifier())
- mangleSourceName(Id);
- else
- mangleOperatorName(Name.getOperator(), UnknownArity);
+ manglePrefix(Dependent->getQualifier());
+
+ if (Clang11Compat && mangleSubstitution(Template))
+ return true;
+
+ if (IdentifierOrOverloadedOperator Name = Dependent->getName();
+ const IdentifierInfo *Id = Name.getIdentifier())
+ mangleSourceName(Id);
+ else
+ mangleOperatorName(Name.getOperator(), UnknownArity);
+
+ addSubstitution(Template);
+ return true;
+ }
+ case clang::TemplateName::SubstTemplateTemplateParm:
+ case clang::TemplateName::AssumedTemplate:
+ case clang::TemplateName::DeducedTemplate:
+ case clang::TemplateName::SubstTemplateTemplateParmPack:
+ case clang::TemplateName::OverloadedTemplate:
+ llvm_unreachable("these shouldn't survive to mangling");
+ break;
+ }
+ llvm_unreachable("unhandled template name kind");
+}
- addSubstitution(Template);
+void CXXNameMangler::mangleNameWithPrefix(GlobalDecl GD,
+ NestedNameSpecifier Qualifier) {
+ if (Qualifier) {
+ Out << 'N';
+ manglePrefix(Qualifier);
+ }
+ mangleName(GD);
+ if (Qualifier)
+ Out << 'E';
}
void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
bool NoFunction) {
- const TemplateDecl *ND = cast<TemplateDecl>(GD.getDecl());
+ const NamedDecl *ND = cast<NamedDecl>(GD.getDecl());
// <template-prefix> ::= <prefix> <template unqualified-name>
// ::= <template-param>
// ::= <substitution>
@@ -2309,9 +2372,12 @@ void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
manglePrefix(DC, NoFunction);
if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
mangleUnqualifiedName(GD, DC, nullptr);
- else
- mangleUnqualifiedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
- nullptr);
+ else {
+ const auto *SD = dyn_cast<UsingShadowDecl>(ND);
+ const NamedDecl *TD =
+ SD ? SD->getTargetDecl() : cast<TemplateDecl>(ND)->getTemplatedDecl();
+ mangleUnqualifiedName(GD.getWithDecl(TD), DC, nullptr);
+ }
}
addSubstitution(ND);
@@ -3008,53 +3074,13 @@ void CXXNameMangler::mangleType(QualType T) {
// augmented via semantic analysis (i.e., with implicit conversions and
// default template arguments) for any instantiation-dependent type.
// Unfortunately, that requires several changes to our AST:
- // - Instantiation-dependent TemplateSpecializationTypes will need to be
- // uniqued, so that we can handle substitutions properly
// - Default template arguments will need to be represented in the
// TemplateSpecializationType, since they need to be mangled even though
// they aren't written.
// - Conversions on non-type template arguments need to be expressed, since
// they can affect the mangling of sizeof/alignof.
- //
- // FIXME: This is wrong when mapping to the canonical type for a dependent
- // type discards instantiation-dependent portions of the type, such as for:
- //
- // template<typename T, int N> void f(T (&)[sizeof(N)]);
- // template<typename T> void f(T() throw(typename T::type)); (pre-C++17)
- //
- // It's also wrong in the opposite direction when instantiation-dependent,
- // canonically-equivalent types differ in some irrelevant portion of inner
- // type sugar. In such cases, we fail to form correct substitutions, eg:
- //
- // template<int N> void f(A<sizeof(N)> *, A<sizeof(N)> (*));
- //
- // We should instead canonicalize the non-instantiation-dependent parts,
- // regardless of whether the type as a whole is dependent or instantiation
- // dependent.
- if (!T->isInstantiationDependentType() || T->isDependentType())
- T = T.getCanonicalType();
- else {
- // Desugar any types that are purely sugar.
- do {
- // Don't desugar through template specialization types that aren't
- // type aliases. We need to mangle the template arguments as written.
- if (const TemplateSpecializationType *TST
- = dyn_cast<TemplateSpecializationType>(T))
- if (!TST->isTypeAlias())
- break;
-
- // FIXME: We presumably shouldn't strip off ElaboratedTypes with
- // instantation-dependent qualifiers. See
- // https://github.com/itanium-cxx-abi/cxx-abi/issues/114.
-
- QualType Desugared
- = T.getSingleStepDesugaredType(Context.getASTContext());
- if (Desugared == T)
- break;
-
- T = Desugared;
- } while (true);
- }
+ T = Context.getASTContext().getCanonicalType(
+ T, CanonicalizationKind::Functional);
auto [ty, quals] = T.split();
bool isSubstitutable =
@@ -3088,10 +3114,6 @@ void CXXNameMangler::mangleType(QualType T) {
} else {
switch (ty->getTypeClass()) {
#define ABSTRACT_TYPE(CLASS, PARENT)
-#define NON_CANONICAL_TYPE(CLASS, PARENT) \
- case Type::CLASS: \
- llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \
- return;
#define TYPE(CLASS, PARENT) \
case Type::CLASS: \
mangleType(static_cast<const CLASS##Type*>(ty)); \
@@ -3799,8 +3821,8 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto,
}
// Mangle the type.
- QualType ParamTy = Proto->getParamType(I);
- mangleType(Context.getASTContext().getSignatureParameterType(ParamTy));
+ mangleType(Context.getASTContext().getCanonicalParamType(
+ Proto->getParamType(I), CanonicalizationKind::Functional));
if (FD) {
if (auto *Attr = FD->getParamDecl(I)->getAttr<PassObjectSizeAttr>()) {
@@ -3828,10 +3850,8 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto,
FunctionTypeDepth.pop(saved);
}
-// <type> ::= <class-enum-type>
-// <class-enum-type> ::= <name>
void CXXNameMangler::mangleType(const UnresolvedUsingType *T) {
- mangleName(T->getDecl());
+ llvm_unreachable("UnresolvedUsingType shouldn't survive to mangling");
}
// <type> ::= <class-enum-type>
@@ -3843,7 +3863,7 @@ void CXXNameMangler::mangleType(const RecordType *T) {
mangleType(static_cast<const TagType*>(T));
}
void CXXNameMangler::mangleType(const TagType *T) {
- mangleName(T->getDecl()->getDefinitionOrSelf());
+ mangleNameWithPrefix(T->getDecl(), T->getQualifier());
}
// <type> ::= <array-type>
@@ -4480,18 +4500,15 @@ void CXXNameMangler::mangleType(const InjectedClassNameType *T) {
}
void CXXNameMangler::mangleType(const TemplateSpecializationType *T) {
- if (TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl()) {
- mangleTemplateName(TD, T->template_arguments());
- } else {
- Out << 'N';
- mangleTemplatePrefix(T->getTemplateName());
-
- // FIXME: GCC does not appear to mangle the template arguments when
- // the template in question is a dependent template name. Should we
- // emulate that badness?
- mangleTemplateArgs(T->getTemplateName(), T->template_arguments());
+ TemplateName Template = T->getTemplateName();
+ bool Nested = mangleTemplatePrefix(Template, /*InPrefix=*/false);
+
+ // FIXME: GCC does not appear to mangle the template arguments when
+ // the template in question is a dependent template name. Should we
+ // emulate that badness?
+ mangleTemplateArgs(Template, T->template_arguments());
+ if (Nested)
Out << 'E';
- }
}
void CXXNameMangler::mangleType(const DependentNameType *T) {
@@ -4658,6 +4675,45 @@ void CXXNameMangler::mangleType(const ArrayParameterType *T) {
mangleType(cast<ConstantArrayType>(T));
}
+void CXXNameMangler::mangleType(const TypedefType *T) {
+ mangleNameWithPrefix(T->getDecl(), T->getQualifier());
+}
+
+void CXXNameMangler::mangleType(const UsingType *T) {
+ mangleNameWithPrefix(T->getDecl(), T->getQualifier());
+}
+
+void CXXNameMangler::mangleType(const DecayedType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const AdjustedType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const AttributedType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const ObjCTypeParamType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const MacroQualifiedType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const CountAttributedType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const PredefinedSugarType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const BTFTagAttributedType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const SubstTemplateTypeParmType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const ParenType *T) {
+ llvm_unreachable("can't mangle non-canonical type");
+}
+
void CXXNameMangler::mangleType(const HLSLAttributedResourceType *T) {
llvm::SmallString<64> Str("_Res");
const HLSLAttributedResourceType::Attributes &Attrs = T->getAttrs();
@@ -7040,8 +7096,9 @@ static bool hasMangledSubstitutionQualifiers(QualType T) {
bool CXXNameMangler::mangleSubstitution(QualType T) {
if (!hasMangledSubstitutionQualifiers(T)) {
- if (const auto *RD = T->getAsCXXRecordDecl())
- return mangleSubstitution(RD);
+ if (const auto *TT = dyn_cast<TagType>(T);
+ isa_and_nonnull<RecordType, InjectedClassNameType>(TT))
+ return mangleSubstitution(TT->getDecl());
}
uintptr_t TypePtr = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr());
@@ -7210,8 +7267,9 @@ bool CXXNameMangler::mangleStandardSubstitution(const NamedDecl *ND) {
void CXXNameMangler::addSubstitution(QualType T) {
if (!hasMangledSubstitutionQualifiers(T)) {
- if (const auto *RD = T->getAsCXXRecordDecl()) {
- addSubstitution(RD);
+ if (const auto *TT = dyn_cast<TagType>(T);
+ isa_and_nonnull<RecordType, InjectedClassNameType>(TT)) {
+ addSubstitution(TT->getDecl());
return;
}
}
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index 3138f95e6a83b..84b18f96f2dc9 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1762,8 +1762,16 @@ void JSONNodeDumper::VisitTemplateExpansionTemplateArgument(
void JSONNodeDumper::VisitExpressionTemplateArgument(
const TemplateArgument &TA) {
JOS.attribute("isExpr", true);
- if (TA.isCanonicalExpr())
- JOS.attribute("isCanonical", true);
+ if (auto Kind = TA.getExprCanonKind()) {
+ switch (*Kind) {
+ case CanonicalizationKind::Structural:
+ JOS.attribute("CanonicalKind", "Structural");
+ break;
+ case CanonicalizationKind::Functional:
+ JOS.attribute("CanonicalKind", "Functional");
+ break;
+ }
+ }
}
void JSONNodeDumper::VisitPackTemplateArgument(const TemplateArgument &TA) {
JOS.attribute("isPack", true);
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..1eab0fc3c2cd1 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -994,14 +994,12 @@ class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> {
}
void VisitDecltypeType(const DecltypeType *T) {
+ ID.AddInteger(T->getExprCanonicalizationKind().toInternalRepresentation());
+ // FIXME: The kind above should affect how the expression is hashed.
Hash.AddStmt(T->getUnderlyingExpr());
VisitType(T);
}
- void VisitDependentDecltypeType(const DependentDecltypeType *T) {
- VisitDecltypeType(T);
- }
-
void VisitDeducedType(const DeducedType *T) {
AddQualType(T->getDeducedType());
VisitType(T);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index bd65f20214791..535725e6cbb9b 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -28,13 +28,16 @@ namespace {
class StmtProfiler : public ConstStmtVisitor<StmtProfiler> {
protected:
llvm::FoldingSetNodeID &ID;
- bool Canonical;
+ const ASTContext *Context;
+ CanonicalizationKindOrNone CanonKind;
bool ProfileLambdaExpr;
+ bool AnyNonCanonical = false;
public:
- StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical,
- bool ProfileLambdaExpr)
- : ID(ID), Canonical(Canonical), ProfileLambdaExpr(ProfileLambdaExpr) {}
+ StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext *Context,
+ CanonicalizationKindOrNone CanonKind, bool ProfileLambdaExpr)
+ : ID(ID), Context(Context), CanonKind(CanonKind),
+ ProfileLambdaExpr(ProfileLambdaExpr) {}
virtual ~StmtProfiler() {}
@@ -46,6 +49,8 @@ namespace {
virtual void HandleStmtClass(Stmt::StmtClass SC) = 0;
+ bool hasAnyNonCanonical() const { return AnyNonCanonical; }
+
#define STMT(Node, Base) void Visit##Node(const Node *S);
#include "clang/AST/StmtNodes.inc"
@@ -81,13 +86,12 @@ namespace {
};
class StmtProfilerWithPointers : public StmtProfiler {
- const ASTContext &Context;
-
public:
StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID,
- const ASTContext &Context, bool Canonical,
+ const ASTContext &Context,
+ CanonicalizationKindOrNone CanonKind,
bool ProfileLambdaExpr)
- : StmtProfiler(ID, Canonical, ProfileLambdaExpr), Context(Context) {}
+ : StmtProfiler(ID, &Context, CanonKind, ProfileLambdaExpr) {}
private:
void HandleStmtClass(Stmt::StmtClass SC) override {
@@ -97,7 +101,7 @@ namespace {
void VisitDecl(const Decl *D) override {
ID.AddInteger(D ? D->getKind() : 0);
- if (Canonical && D) {
+ if (CanonKind && D) {
if (const NonTypeTemplateParmDecl *NTTP =
dyn_cast<NonTypeTemplateParmDecl>(D)) {
ID.AddInteger(NTTP->getDepth());
@@ -111,7 +115,7 @@ namespace {
//
// TODO: Why do we need to include the type in the profile? It's not
// part of the mangling.
- VisitType(Context.getUnconstrainedType(NTTP->getType()));
+ VisitType(Context->getUnconstrainedType(NTTP->getType()));
return;
}
@@ -153,8 +157,8 @@ namespace {
}
void VisitType(QualType T) override {
- if (Canonical && !T.isNull())
- T = Context.getCanonicalType(T);
+ if (CanonKind && !T.isNull())
+ T = Context->getCanonicalType(T, *CanonKind, AnyNonCanonical);
ID.AddPointer(T.getAsOpaquePtr());
}
@@ -168,14 +172,16 @@ namespace {
}
void VisitNestedNameSpecifier(NestedNameSpecifier NNS) override {
- if (Canonical)
- NNS = NNS.getCanonical();
+ if (CanonKind)
+ NNS = Context->getCanonicalNestedNameSpecifier(NNS, *CanonKind,
+ AnyNonCanonical);
NNS.Profile(ID);
}
void VisitTemplateName(TemplateName Name) override {
- if (Canonical)
- Name = Context.getCanonicalTemplateName(Name);
+ if (CanonKind)
+ Name = Context->getCanonicalTemplateName(Name, /*IgnoreDeduced=*/false,
+ *CanonKind, AnyNonCanonical);
Name.Profile(ID);
}
@@ -185,7 +191,8 @@ namespace {
ODRHash &Hash;
public:
StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
- : StmtProfiler(ID, /*Canonical=*/false, /*ProfileLambdaExpr=*/false),
+ : StmtProfiler(ID, /*Context=*/nullptr, /*CanonKind=*/std::nullopt,
+ /*ProfileLambdaExpr=*/false),
Hash(Hash) {}
private:
@@ -1404,10 +1411,10 @@ void StmtProfiler::VisitConstantExpr(const ConstantExpr *S) {
void StmtProfiler::VisitDeclRefExpr(const DeclRefExpr *S) {
VisitExpr(S);
- if (!Canonical)
+ if (!CanonKind)
VisitNestedNameSpecifier(S->getQualifier());
VisitDecl(S->getDecl());
- if (!Canonical) {
+ if (!CanonKind) {
ID.AddBoolean(S->hasExplicitTemplateArgs());
if (S->hasExplicitTemplateArgs())
VisitTemplateArguments(S->getTemplateArgs(), S->getNumTemplateArgs());
@@ -1440,8 +1447,8 @@ void StmtProfiler::VisitIntegerLiteral(const IntegerLiteral *S) {
S->getValue().Profile(ID);
QualType T = S->getType();
- if (Canonical)
- T = T.getCanonicalType();
+ if (CanonKind)
+ T = Context->getCanonicalType(T, *CanonKind, AnyNonCanonical);
ID.AddInteger(T->getTypeClass());
if (auto BitIntT = T->getAs<BitIntType>())
BitIntT->Profile(ID);
@@ -1561,7 +1568,7 @@ void StmtProfiler::VisitCallExpr(const CallExpr *S) {
void StmtProfiler::VisitMemberExpr(const MemberExpr *S) {
VisitExpr(S);
VisitDecl(S->getMemberDecl());
- if (!Canonical)
+ if (!CanonKind)
VisitNestedNameSpecifier(S->getQualifier());
ID.AddBoolean(S->isArrow());
}
@@ -1569,6 +1576,7 @@ void StmtProfiler::VisitMemberExpr(const MemberExpr *S) {
void StmtProfiler::VisitCompoundLiteralExpr(const CompoundLiteralExpr *S) {
VisitExpr(S);
ID.AddBoolean(S->isFileScope());
+ VisitType(S->getTypeSourceInfo()->getType());
}
void StmtProfiler::VisitCastExpr(const CastExpr *S) {
@@ -1615,6 +1623,7 @@ void StmtProfiler::VisitAddrLabelExpr(const AddrLabelExpr *S) {
void StmtProfiler::VisitStmtExpr(const StmtExpr *S) {
VisitExpr(S);
+ ID.AddInteger(S->getTemplateDepth());
}
void StmtProfiler::VisitShuffleVectorExpr(const ShuffleVectorExpr *S) {
@@ -2216,6 +2225,7 @@ void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) {
void
StmtProfiler::VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *S) {
VisitExpr(S);
+ VisitType(S->getTypeSourceInfo()->getType());
}
void StmtProfiler::VisitCXXDeleteExpr(const CXXDeleteExpr *S) {
@@ -2256,6 +2266,7 @@ void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {
VisitExpr(S);
bool DescribingDependentVarTemplate =
S->getNumDecls() == 1 && isa<VarTemplateDecl>(*S->decls_begin());
+ ID.AddBoolean(DescribingDependentVarTemplate);
if (DescribingDependentVarTemplate) {
VisitDecl(*S->decls_begin());
} else {
@@ -2978,10 +2989,21 @@ void StmtProfiler::VisitHLSLOutArgExpr(const HLSLOutArgExpr *S) {
VisitStmt(S);
}
-void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
- bool Canonical, bool ProfileLambdaExpr) const {
- StmtProfilerWithPointers Profiler(ID, Context, Canonical, ProfileLambdaExpr);
+CanonicalizationKindOrNone Stmt::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Context,
+ CanonicalizationKindOrNone CanonKind,
+ bool ProfileLambdaExpr) const {
+ StmtProfilerWithPointers Profiler(ID, Context, CanonKind, ProfileLambdaExpr);
Profiler.Visit(this);
+
+ // FIXME: The profiler does not yet support returning the maximum
+ // canonicalization kind for the no-canonicalization case.
+ if (!CanonKind)
+ return std::nullopt;
+ auto Result = Profiler.hasAnyNonCanonical()
+ ? std::max(CanonicalizationKind::Functional, *CanonKind)
+ : CanonicalizationKind::Structural;
+ return Result;
}
void Stmt::ProcessODRHash(llvm::FoldingSetNodeID &ID,
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index a5e0dde45364c..100e91f454d84 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -426,13 +426,9 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
break;
case Expression: {
- const Expr *E = getAsExpr();
- bool IsCanonical = isCanonicalExpr();
- ID.AddBoolean(IsCanonical);
- if (IsCanonical)
- E->Profile(ID, Context, true);
- else
- ID.AddPointer(E);
+ CanonicalizationKindOrNone Kind = getExprCanonKind();
+ ID.AddInteger(Kind.toInternalRepresentation());
+ getAsExpr()->Profile(ID, Context, Kind);
break;
}
@@ -453,7 +449,7 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const {
return TypeOrValue.V == Other.TypeOrValue.V;
case Expression:
return TypeOrValue.V == Other.TypeOrValue.V &&
- TypeOrValue.IsCanonicalExpr == Other.TypeOrValue.IsCanonicalExpr;
+ TypeOrValue.ExprCanonKind == Other.TypeOrValue.ExprCanonKind;
case Template:
case TemplateExpansion:
@@ -499,7 +495,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const {
case Expression:
return TemplateArgument(cast<PackExpansionExpr>(getAsExpr())->getPattern(),
- isCanonicalExpr());
+ getExprCanonKind());
case TemplateExpansion:
return TemplateArgument(getAsTemplateOrTemplatePattern());
@@ -572,7 +568,7 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
case Expression: {
PrintingPolicy ExprPolicy = Policy;
- ExprPolicy.PrintAsCanonical = isCanonicalExpr();
+ ExprPolicy.PrintAsCanonical = bool(getExprCanonKind());
getAsExpr()->printPretty(Out, nullptr, ExprPolicy);
break;
}
diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp
index 797a354c5d0fa..a1e90fdc78301 100644
--- a/clang/lib/AST/TemplateName.cpp
+++ b/clang/lib/AST/TemplateName.cpp
@@ -361,12 +361,7 @@ TemplateNameDependence TemplateName::getDependence() const {
if (TTP->isParameterPack())
D |= TemplateNameDependence::UnexpandedPack;
}
- // FIXME: Hack, getDeclContext() can be null if Template is still
- // initializing due to PCH reading, so we check it before using it.
- // Should probably modify TemplateSpecializationType to allow constructing
- // it without the isDependent() checking.
- if (Template->getDeclContext() &&
- Template->getDeclContext()->isDependentContext())
+ if (Template->getDeclContext()->isDependentContext())
D |= TemplateNameDependence::DependentInstantiation;
return D;
}
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 250ec8b666e05..4648e1517a0d2 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1394,8 +1394,16 @@ void TextNodeDumper::VisitTemplateExpansionTemplateArgument(
void TextNodeDumper::VisitExpressionTemplateArgument(
const TemplateArgument &TA) {
OS << " expr";
- if (TA.isCanonicalExpr())
- OS << " canonical";
+ if (auto Kind = TA.getExprCanonKind()) {
+ switch (*Kind) {
+ case CanonicalizationKind::Structural:
+ OS << " canonical";
+ break;
+ case CanonicalizationKind::Functional:
+ OS << " canonical-functional";
+ break;
+ }
+ }
dumpTemplateArgument(TA);
}
@@ -2151,6 +2159,19 @@ void TextNodeDumper::VisitFunctionProtoType(const FunctionProtoType *T) {
VisitFunctionType(T);
}
+void TextNodeDumper::VisitDecltypeType(const DecltypeType *T) {
+ if (auto K = T->getExprCanonicalizationKind()) {
+ switch (*K) {
+ case CanonicalizationKind::Structural:
+ OS << " equiv_expr";
+ break;
+ case CanonicalizationKind::Functional:
+ OS << " fequiv_expr";
+ break;
+ }
+ }
+}
+
void TextNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *T) {
if (ElaboratedTypeKeyword K = T->getKeyword();
K != ElaboratedTypeKeyword::None)
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 1cc318697d936..fdb84d1e0be18 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -275,7 +275,7 @@ void ConstantArrayType::Profile(llvm::FoldingSetNodeID &ID,
ID.AddInteger(TypeQuals);
ID.AddBoolean(SizeExpr != nullptr);
if (SizeExpr)
- SizeExpr->Profile(ID, Context, true);
+ SizeExpr->Profile(ID, Context, CanonicalizationKind::Structural);
}
QualType ArrayParameterType::getConstantArrayType(const ASTContext &Ctx) const {
@@ -297,7 +297,7 @@ void DependentSizedArrayType::Profile(llvm::FoldingSetNodeID &ID,
ID.AddInteger(llvm::to_underlying(SizeMod));
ID.AddInteger(TypeQuals);
if (E)
- E->Profile(ID, Context, true);
+ E->Profile(ID, Context, CanonicalizationKind::Structural);
}
DependentVectorType::DependentVectorType(QualType ElementType,
@@ -318,7 +318,7 @@ void DependentVectorType::Profile(llvm::FoldingSetNodeID &ID,
VectorKind VecKind) {
ID.AddPointer(ElementType.getAsOpaquePtr());
ID.AddInteger(llvm::to_underlying(VecKind));
- SizeExpr->Profile(ID, Context, true);
+ SizeExpr->Profile(ID, Context, CanonicalizationKind::Structural);
}
DependentSizedExtVectorType::DependentSizedExtVectorType(QualType ElementType,
@@ -337,7 +337,7 @@ void DependentSizedExtVectorType::Profile(llvm::FoldingSetNodeID &ID,
QualType ElementType,
Expr *SizeExpr) {
ID.AddPointer(ElementType.getAsOpaquePtr());
- SizeExpr->Profile(ID, Context, true);
+ SizeExpr->Profile(ID, Context, CanonicalizationKind::Structural);
}
DependentAddressSpaceType::DependentAddressSpaceType(QualType PointeeType,
@@ -356,7 +356,7 @@ void DependentAddressSpaceType::Profile(llvm::FoldingSetNodeID &ID,
QualType PointeeType,
Expr *AddrSpaceExpr) {
ID.AddPointer(PointeeType.getAsOpaquePtr());
- AddrSpaceExpr->Profile(ID, Context, true);
+ AddrSpaceExpr->Profile(ID, Context, CanonicalizationKind::Structural);
}
MatrixType::MatrixType(TypeClass tc, QualType matrixType, QualType canonType,
@@ -402,8 +402,8 @@ void DependentSizedMatrixType::Profile(llvm::FoldingSetNodeID &ID,
QualType ElementType, Expr *RowExpr,
Expr *ColumnExpr) {
ID.AddPointer(ElementType.getAsOpaquePtr());
- RowExpr->Profile(ID, CTX, true);
- ColumnExpr->Profile(ID, CTX, true);
+ RowExpr->Profile(ID, CTX, CanonicalizationKind::Structural);
+ ColumnExpr->Profile(ID, CTX, CanonicalizationKind::Structural);
}
VectorType::VectorType(QualType vecType, unsigned nElements, QualType canonType,
@@ -428,9 +428,16 @@ BitIntType::BitIntType(bool IsUnsigned, unsigned NumBits)
NumBits(NumBits) {}
DependentBitIntType::DependentBitIntType(bool IsUnsigned, Expr *NumBitsExpr)
+ // DependentBitIntType must always be type-dependent.
+ // The expression must be value-dependent, so will also be
+ // instantiation-dependent.
: Type(DependentBitInt, QualType{},
- toTypeDependence(NumBitsExpr->getDependence())),
- ExprAndUnsigned(NumBitsExpr, IsUnsigned) {}
+ toTypeDependence(NumBitsExpr->getDependence()) |
+ TypeDependence::Dependent),
+ ExprAndUnsigned(NumBitsExpr, IsUnsigned) {
+ assert(NumBitsExpr->isValueDependent() &&
+ "NumBitsExpr must be value-dependent");
+}
bool DependentBitIntType::isUnsigned() const {
return ExprAndUnsigned.getInt();
@@ -444,7 +451,7 @@ void DependentBitIntType::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Context, bool IsUnsigned,
Expr *NumBitsExpr) {
ID.AddBoolean(IsUnsigned);
- NumBitsExpr->Profile(ID, Context, true);
+ NumBitsExpr->Profile(ID, Context, CanonicalizationKind::Structural);
}
bool BoundsAttributedType::referencesFieldDecls() const {
@@ -4000,7 +4007,10 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
for (QualType Ex : epi.ExceptionSpec.Exceptions)
ID.AddPointer(Ex.getAsOpaquePtr());
} else if (isComputedNoexcept(epi.ExceptionSpec.Type)) {
- epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, Canonical);
+ epi.ExceptionSpec.NoexceptExpr->Profile(
+ ID, Context,
+ Canonical ? CanonicalizationKindOrNone(CanonicalizationKind::Structural)
+ : std::nullopt);
} else if (epi.ExceptionSpec.Type == EST_Uninstantiated ||
epi.ExceptionSpec.Type == EST_Unevaluated) {
ID.AddPointer(epi.ExceptionSpec.SourceDecl->getCanonicalDecl());
@@ -4104,13 +4114,10 @@ TypedefType::TypedefType(TypeClass TC, ElaboratedTypeKeyword Keyword,
NestedNameSpecifier Qualifier,
const TypedefNameDecl *D, QualType UnderlyingType,
bool HasTypeDifferentFromDecl)
- : TypeWithKeyword(
- Keyword, TC, UnderlyingType.getCanonicalType(),
- toSemanticDependence(UnderlyingType->getDependence()) |
- (Qualifier
- ? toTypeDependence(Qualifier.getDependence() &
- ~NestedNameSpecifierDependence::Dependent)
- : TypeDependence{})),
+ : TypeWithKeyword(Keyword, TC, UnderlyingType.getCanonicalType(),
+ toSemanticDependence(UnderlyingType->getDependence()) |
+ toSyntacticDependence(
+ toTypeDependence(Qualifier.getDependence()))),
Decl(const_cast<TypedefNameDecl *>(D)) {
if ((TypedefBits.hasQualifier = !!Qualifier))
*getTrailingObjects<NestedNameSpecifier>() = Qualifier;
@@ -4127,13 +4134,10 @@ UnresolvedUsingType::UnresolvedUsingType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier Qualifier,
const UnresolvedUsingTypenameDecl *D,
const Type *CanonicalType)
- : TypeWithKeyword(
- Keyword, UnresolvedUsing, QualType(CanonicalType, 0),
- TypeDependence::DependentInstantiation |
- (Qualifier
- ? toTypeDependence(Qualifier.getDependence() &
- ~NestedNameSpecifierDependence::Dependent)
- : TypeDependence{})),
+ : TypeWithKeyword(Keyword, UnresolvedUsing, QualType(CanonicalType, 0),
+ TypeDependence::DependentInstantiation |
+ toSyntacticDependence(
+ toTypeDependence(Qualifier.getDependence()))),
Decl(const_cast<UnresolvedUsingTypenameDecl *>(D)) {
if ((UnresolvedUsingBits.hasQualifier = !!Qualifier))
*getTrailingObjects<NestedNameSpecifier>() = Qualifier;
@@ -4143,7 +4147,9 @@ UsingType::UsingType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier Qualifier, const UsingShadowDecl *D,
QualType UnderlyingType)
: TypeWithKeyword(Keyword, Using, UnderlyingType.getCanonicalType(),
- toSemanticDependence(UnderlyingType->getDependence())),
+ toSemanticDependence(UnderlyingType->getDependence()) |
+ toSyntacticDependence(
+ toTypeDependence(Qualifier.getDependence()))),
D(const_cast<UsingShadowDecl *>(D)), UnderlyingType(UnderlyingType) {
if ((UsingBits.hasQualifier = !!Qualifier))
*getTrailingObjects() = Qualifier;
@@ -4193,7 +4199,7 @@ QualType TypeOfExprType::desugar() const {
void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Context, Expr *E,
bool IsUnqual) {
- E->Profile(ID, Context, true);
+ E->Profile(ID, Context, CanonicalizationKind::Structural);
ID.AddBoolean(IsUnqual);
}
@@ -4215,19 +4221,17 @@ QualType TypeOfType::desugar() const {
: QT;
}
-DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
+DecltypeType::DecltypeType(Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+ QualType UnderlyingType, QualType CanonType)
// C++11 [temp.type]p2: "If an expression e involves a template parameter,
// decltype(e) denotes a unique dependent type." Hence a decltype type is
// type-dependent even if its expression is only instantiation-dependent.
- : Type(Decltype, can,
- toTypeDependence(E->getDependence()) |
- (E->isInstantiationDependent() ? TypeDependence::Dependent
- : TypeDependence::None) |
- (E->getType()->getDependence() &
- TypeDependence::VariablyModified)),
- E(E), UnderlyingType(underlyingType) {}
+ : Type(Decltype, CanonType, toTypeDependence(E->getDependence())), E(E),
+ UnderlyingType(UnderlyingType) {
+ DecltypeTypeBits.ExprCanonKind = ExprCanonKind.toInternalRepresentation();
+}
-bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); }
+bool DecltypeType::isSugared() const { return !E->isTypeDependent(); }
QualType DecltypeType::desugar() const {
if (isSugared())
@@ -4236,12 +4240,13 @@ QualType DecltypeType::desugar() const {
return QualType(this, 0);
}
-DependentDecltypeType::DependentDecltypeType(Expr *E)
- : DecltypeType(E, QualType()) {}
-
-void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID,
- const ASTContext &Context, Expr *E) {
- E->Profile(ID, Context, true);
+void DecltypeType::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Context, Expr *E,
+ CanonicalizationKindOrNone ExprCanonKind,
+ QualType UnderlyingType) {
+ ID.AddInteger(ExprCanonKind.toInternalRepresentation());
+ E->Profile(ID, Context, ExprCanonKind);
+ UnderlyingType.Profile(ID);
}
PackIndexingType::PackIndexingType(QualType Canonical, QualType Pattern,
@@ -4303,7 +4308,7 @@ void PackIndexingType::Profile(llvm::FoldingSetNodeID &ID,
Expr *E, bool FullySubstituted,
ArrayRef<QualType> Expansions) {
- E->Profile(ID, Context, true);
+ E->Profile(ID, Context, CanonicalizationKind::Structural);
ID.AddBoolean(FullySubstituted);
if (!Expansions.empty()) {
ID.AddInteger(Expansions.size());
@@ -4654,29 +4659,25 @@ bool TemplateSpecializationType::anyInstantiationDependentTemplateArguments(
return false;
}
-static TypeDependence
-getTemplateSpecializationTypeDependence(QualType Underlying, TemplateName T) {
- TypeDependence D = Underlying.isNull()
- ? TypeDependence::DependentInstantiation
- : toSemanticDependence(Underlying->getDependence());
- D |= toTypeDependence(T.getDependence()) & TypeDependence::UnexpandedPack;
- if (isPackProducingBuiltinTemplateName(T)) {
- if (Underlying.isNull()) // Dependent, will produce a pack on substitution.
- D |= TypeDependence::UnexpandedPack;
- else
- D |= (Underlying->getDependence() & TypeDependence::UnexpandedPack);
- }
- return D;
-}
-
TemplateSpecializationType::TemplateSpecializationType(
ElaboratedTypeKeyword Keyword, TemplateName T, bool IsAlias,
ArrayRef<TemplateArgument> Args, QualType Underlying)
- : TypeWithKeyword(Keyword, TemplateSpecialization,
- Underlying.isNull() ? QualType(this, 0)
- : Underlying.getCanonicalType(),
- getTemplateSpecializationTypeDependence(Underlying, T)),
+ : TypeWithKeyword(
+ Keyword, TemplateSpecialization,
+ Underlying.isNull() ? QualType(this, 0)
+ : Underlying.getCanonicalType(),
+ toSyntacticDependence(toTypeDependence(T.getDependence()))),
Template(T) {
+ addDependence(Underlying.isNull()
+ ? TypeDependence::DependentInstantiation
+ : toSemanticDependence(Underlying->getDependence()));
+
+ // FIXME: Ugly hack, pack producing templates break the syntactic/semantic
+ // dependence distinction.
+ if ((Underlying.isNull() || Underlying->containsUnexpandedParameterPack()) &&
+ isPackProducingBuiltinTemplateName(T))
+ addDependence(TypeDependence::UnexpandedPack);
+
TemplateSpecializationTypeBits.NumArgs = Args.size();
TemplateSpecializationTypeBits.TypeAlias = IsAlias;
@@ -4711,23 +4712,30 @@ QualType TemplateSpecializationType::getAliasedType() const {
}
bool clang::TemplateSpecializationType::isSugared() const {
- return !isDependentType() || isCurrentInstantiation() || isTypeAlias() ||
- (isPackProducingBuiltinTemplateName(Template) &&
- isa<SubstBuiltinTemplatePackType>(*getCanonicalTypeInternal()));
+ return isTypeAlias() ||
+ !isa<TemplateSpecializationType>(getCanonicalTypeInternal());
}
void TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Ctx) {
- Profile(ID, getKeyword(), Template, template_arguments(),
- isSugared() ? desugar() : QualType(), Ctx);
+ Profile(ID, getKeyword(), Template, template_arguments(), isTypeAlias(),
+ desugar(), Ctx);
}
void TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID,
ElaboratedTypeKeyword Keyword,
TemplateName T,
ArrayRef<TemplateArgument> Args,
- QualType Underlying,
+ bool IsTypeAlias, QualType Underlying,
const ASTContext &Context) {
+ assert(IsTypeAlias || Underlying.isNull() || Underlying.isCanonical());
+ if (!Underlying.isNull()) {
+ if (!IsTypeAlias && isa<TemplateSpecializationType>(Underlying))
+ Underlying = QualType();
+ } else {
+ assert(!IsTypeAlias);
+ }
+
ID.AddInteger(llvm::to_underlying(Keyword));
T.Profile(ID);
Underlying.Profile(ID);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 80f5b90ba35c4..8c22478b6d847 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1339,7 +1339,8 @@ void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) {
OS << "decltype(";
if (const Expr *E = T->getUnderlyingExpr()) {
PrintingPolicy ExprPolicy = Policy;
- ExprPolicy.PrintAsCanonical = T->isCanonicalUnqualified();
+ // FIXME: Support printing as functionally equivalent canonical type.
+ ExprPolicy.PrintAsCanonical = bool(T->getExprCanonicalizationKind());
E->printPretty(OS, nullptr, ExprPolicy);
}
OS << ')';
diff --git a/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp
index dee29fe004f42..9e88364f9a067 100644
--- a/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp
+++ b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp
@@ -883,7 +883,8 @@ RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) {
ICIS_NoInit);
MemberExpr *ME = MemberExpr::CreateImplicit(
*Context, PE, true, FD, FD->getType(), VK_LValue, OK_Ordinary);
- IvarT = Context->getDecltypeType(ME, ME->getType());
+ IvarT = Context->getDecltypeType(ME, /*ExprCanonKind=*/std::nullopt,
+ ME->getType());
}
}
convertObjCTypeToCStyleType(IvarT);
@@ -7484,7 +7485,8 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
/*Mutable=*/true, ICIS_NoInit);
MemberExpr *ME = MemberExpr::CreateImplicit(
*Context, PE, true, FD, FD->getType(), VK_LValue, OK_Ordinary);
- IvarT = Context->getDecltypeType(ME, ME->getType());
+ IvarT = Context->getDecltypeType(ME, /*ExprCanonKind=*/std::nullopt,
+ ME->getType());
}
}
convertObjCTypeToCStyleType(IvarT);
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 235ede8eb0bf0..4abab547d667c 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -308,16 +308,11 @@ addVectorTexturePartialSpecialization(Sema &S, NamespaceDecl *HLSLNamespace,
SourceLocation());
// Create the partial specialization declaration.
- QualType CanonInjectedTST =
- AST.getCanonicalType(AST.getTemplateSpecializationType(
- ElaboratedTypeKeyword::Class, TemplateName(TextureTemplate),
- {TemplateArgument(VectorType)}, {}));
-
auto *PartialSpec = ClassTemplatePartialSpecializationDecl::Create(
AST, TagDecl::TagKind::Class, HLSLNamespace, SourceLocation(),
SourceLocation(), TemplateParams, TextureTemplate,
{TemplateArgument(VectorType)},
- CanQualType::CreateUnsafe(CanonInjectedTST), nullptr);
+ /*PrevDecl=*/nullptr);
// Set the template arguments as written.
TemplateArgument Arg(VectorType);
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 255c22d9c2a31..333405430a1d9 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -61,18 +61,22 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
if (EnteringContext) {
if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
return nullptr;
- const Type *NNSType = NNS.getAsType();
+
+ bool AnyNonCanonical = false;
+ QualType NNSType = Context.getCanonicalType(
+ QualType(NNS.getAsType(), 0), CanonicalizationKind::Functional,
+ AnyNonCanonical);
// Look through type alias templates, per C++0x [temp.dep.type]p1.
- NNSType = Context.getCanonicalType(NNSType);
if (const auto *SpecType =
- dyn_cast<TemplateSpecializationType>(NNSType)) {
+ NNSType->getAsNonAliasTemplateSpecializationType()) {
// We are entering the context of the nested name specifier, so try to
// match the nested name specifier to either a primary class template
// or a class template partial specialization.
if (ClassTemplateDecl *ClassTemplate =
dyn_cast_or_null<ClassTemplateDecl>(
- SpecType->getTemplateName().getAsTemplateDecl())) {
+ SpecType->getTemplateName().getAsTemplateDecl(
+ /*IgnoreDeduced=*/true))) {
// FIXME: The fallback on the search of partial
// specialization using ContextType should be eventually removed since
// it doesn't handle the case of constrained template parameters
@@ -88,9 +92,33 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
return TPL->getDepth() == Depth;
});
if (L != TemplateParamLists.end()) {
+ SmallVector<TemplateArgument, 8> SpecArgs;
+ if (AnyNonCanonical) {
+ // FIXME: Horrid hack to get back a converted template argument
+ // list.
+ TemplateArgumentListInfo TemplateArgs;
+ for (const auto &Arg : SpecType->template_arguments())
+ TemplateArgs.addArgument(getTrivialTemplateArgumentLoc(
+ Arg, /*NTTPType=*/QualType(), SourceLocation()));
+ CheckTemplateArgumentInfo CTAI;
+ DefaultArguments DefaultArgs;
+ SFINAETrap Trap(*this);
+ [[maybe_unused]] bool Res = CheckTemplateArgumentList(
+ ClassTemplate, ClassTemplate->getTemplateParameters(),
+ SourceLocation(), TemplateArgs, DefaultArgs,
+ /*PartialTemplateArgs=*/false, CTAI);
+ assert(
+ !Res && !Trap.hasErrorOccurred() &&
+ "template argument list should have been checked already");
+ SpecArgs = std::move(CTAI.SugaredConverted);
+ Context.canonicalizeTemplateArguments(
+ SpecArgs, CanonicalizationKind::Functional);
+ }
+
void *Pos = nullptr;
PartialSpec = ClassTemplate->findPartialSpecialization(
- SpecType->template_arguments(), *L, Pos);
+ AnyNonCanonical ? SpecArgs : SpecType->template_arguments(),
+ *L, Pos);
}
} else {
PartialSpec =
@@ -119,9 +147,9 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
if (Context.hasSameType(Injected, QualType(SpecType, 0)))
return ClassTemplate->getTemplatedDecl();
}
- } else if (const auto *RecordT = dyn_cast<RecordType>(NNSType)) {
+ } else if (auto *RD = NNSType->getAsCXXRecordDecl()) {
// The nested name specifier refers to a member of a class template.
- return RecordT->getDecl()->getDefinitionOrSelf();
+ return RD;
}
}
@@ -853,6 +881,7 @@ bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
TypeLocBuilder TLB;
DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
+ DecltypeTL.setUnderlyingExpr(DS.getRepAsExpr());
DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
SS.Make(Context, TLB.getTypeLocInContext(Context, T), ColonColonLoc);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index d53c3b6ab2674..94e46543dd88e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -10878,10 +10878,10 @@ bool Sema::CheckSizeofMemaccessArgument(const Expr *LenExpr, const Expr *Dest,
QualType PointeeTy = DestPtrTy->getPointeeType();
if (SizeOfArgID == llvm::FoldingSetNodeID())
- SizeOfArg->Profile(SizeOfArgID, Context, true);
+ SizeOfArg->Profile(SizeOfArgID, Context, CanonicalizationKind::Structural);
llvm::FoldingSetNodeID DestID;
- Dest->Profile(DestID, Context, true);
+ Dest->Profile(DestID, Context, CanonicalizationKind::Structural);
if (DestID == SizeOfArgID) {
// TODO: For strncpy() and friends, this could suggest sizeof(dst)
// over sizeof(src) as well.
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 3f04922a5647e..3d9505a00d9c2 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -182,7 +182,7 @@ struct SatisfactionStackRAII {
static bool DiagRecursiveConstraintEval(
Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E,
const MultiLevelTemplateArgumentList *MLTAL = nullptr) {
- E->Profile(ID, S.Context, /*Canonical=*/true);
+ E->Profile(ID, S.Context, CanonicalizationKind::Functional);
if (MLTAL) {
for (const auto &List : *MLTAL)
for (const auto &TemplateArg : List.Args)
@@ -1627,10 +1627,8 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
return false;
}
- llvm::FoldingSetNodeID ID1, ID2;
- OldConstr->Profile(ID1, Context, /*Canonical=*/true);
- NewConstr->Profile(ID2, Context, /*Canonical=*/true);
- return ID1 == ID2;
+ return Context.hasSameExpr(OldConstr, NewConstr,
+ CanonicalizationKind::Functional);
}
bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) {
@@ -2620,11 +2618,8 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
return true;
// Not the same source level expression - are the expressions
- // identical?
- llvm::FoldingSetNodeID IDA, IDB;
- EA->Profile(IDA, Context, /*Canonical=*/true);
- EB->Profile(IDB, Context, /*Canonical=*/true);
- if (IDA != IDB)
+ // functionally equivalent?
+ if (!Context.hasSameExpr(EA, EB, CanonicalizationKind::Functional))
return false;
AmbiguousAtomic1 = EA;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9f8fc5a187b0a..832f8590f35fc 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2549,10 +2549,9 @@ bool Sema::isIncompatibleTypedef(const TypeDecl *Old, TypedefNameDecl *New) {
return true;
}
- if (OldType != NewType &&
- !OldType->isDependentType() &&
+ if (OldType != NewType && !OldType->isDependentType() &&
!NewType->isDependentType() &&
- !Context.hasSameType(OldType, NewType)) {
+ !Context.hasFunctionallyEquivalentType(OldType, NewType)) {
int Kind = isa<TypeAliasDecl>(Old) ? 1 : 0;
Diag(New->getLocation(), diag::err_redefinition_different_typedef)
<< Kind << NewType << OldType;
@@ -4553,7 +4552,8 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
}
static void diagnoseVarDeclTypeMismatch(Sema &S, VarDecl *New, VarDecl* Old) {
- assert(!S.Context.hasSameType(New->getType(), Old->getType()));
+ assert(
+ !S.Context.hasFunctionallyEquivalentType(New->getType(), Old->getType()));
S.Diag(New->getLocation(), New->isThisDeclarationADefinition()
? diag::err_redefinition_different_type
@@ -4578,7 +4578,8 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
if (New->getType()->isUndeducedType()) {
// We don't know what the new type is until the initializer is attached.
return;
- } else if (Context.hasSameType(New->getType(), Old->getType())) {
+ }
+ if (Context.hasFunctionallyEquivalentType(New->getType(), Old->getType())) {
// These could still be something that needs exception specs checked.
return MergeVarDeclExceptionSpecs(New, Old);
}
@@ -4587,7 +4588,7 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
// object or function shall be identical, except that declarations for an
// array object can specify array types that differ by the presence or
// absence of a major array bound (8.3.4).
- else if (Old->getType()->isArrayType() && New->getType()->isArrayType()) {
+ if (Old->getType()->isArrayType() && New->getType()->isArrayType()) {
const ArrayType *OldArray = Context.getAsArrayType(Old->getType());
const ArrayType *NewArray = Context.getAsArrayType(New->getType());
@@ -4601,26 +4602,28 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
if (PrevVDTy->isIncompleteArrayType() || PrevVDTy->isDependentType())
continue;
- if (!Context.hasSameType(New->getType(), PrevVDTy))
+ if (!Context.hasFunctionallyEquivalentType(New->getType(), PrevVDTy))
return diagnoseVarDeclTypeMismatch(*this, New, PrevVD);
}
}
- if (OldArray->isIncompleteArrayType() && NewArray->isArrayType()) {
- if (Context.hasSameType(OldArray->getElementType(),
- NewArray->getElementType()))
- MergedT = New->getType();
- }
- // FIXME: Check visibility. New is hidden but has a complete type. If New
- // has no array bound, it should not inherit one from Old, if Old is not
- // visible.
- else if (OldArray->isArrayType() && NewArray->isIncompleteArrayType()) {
- if (Context.hasSameType(OldArray->getElementType(),
- NewArray->getElementType()))
- MergedT = Old->getType();
+ if (NewArray->isIncompleteArrayType() ||
+ OldArray->isIncompleteArrayType()) {
+ if (Context.hasFunctionallyEquivalentType(OldArray->getElementType(),
+ NewArray->getElementType())) {
+ if (OldArray->isIncompleteArrayType()) {
+ MergedT = New->getType();
+ } else {
+ // FIXME: Check visibility. New is hidden but has a complete type.
+ // If New has no array bound, it should not inherit one from Old, if
+ // Old is not visible.
+ // FIXME: Rebuild array type with new element type, in order to
+ // preserve type sugar.
+ MergedT = Old->getType().getCanonicalType();
+ }
+ }
}
- }
- else if (New->getType()->isObjCObjectPointerType() &&
+ } else if (New->getType()->isObjCObjectPointerType() &&
Old->getType()->isObjCObjectPointerType()) {
MergedT = Context.mergeObjCGCQualifiers(New->getType(),
Old->getType());
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 6647e52535114..24a2728e22154 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -13903,8 +13903,8 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,
TypeAliasDecl *OldTD = OldDecl->getTemplatedDecl();
if (!Invalid &&
- !Context.hasSameType(OldTD->getUnderlyingType(),
- NewTD->getUnderlyingType())) {
+ !Context.hasFunctionallyEquivalentType(
+ OldTD->getUnderlyingType(), NewTD->getUnderlyingType())) {
// FIXME: The C++0x standard does not clearly say this is ill-formed,
// but we can't reasonably accept it.
Diag(NewTD->getLocation(), diag::err_redefinition_different_typedef)
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 40d530a1f3925..6ce2420db0707 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -580,13 +580,10 @@ static bool CheckEquivalentExceptionSpecImpl(
// C++14 [except.spec]p3:
// Two exception-specifications are compatible if [...] both have the form
// noexcept(constant-expression) and the constant-expressions are equivalent
- if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept) {
- llvm::FoldingSetNodeID OldFSN, NewFSN;
- Old->getNoexceptExpr()->Profile(OldFSN, S.Context, true);
- New->getNoexceptExpr()->Profile(NewFSN, S.Context, true);
- if (OldFSN == NewFSN)
+ if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept)
+ if (S.Context.hasSameExpr(Old->getNoexceptExpr(), New->getNoexceptExpr(),
+ CanonicalizationKind::Functional))
return false;
- }
// Dynamic exception specifications with the same set of adjusted types
// are compatible.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index cf235095d489d..32b35e823479a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2349,7 +2349,7 @@ Sema::ActOnStringLiteral(ArrayRef<Token> StringToks, Scope *UDLScope) {
case LOLR_Template: {
TemplateArgumentListInfo ExplicitArgs;
- TemplateArgument Arg(Lit, /*IsCanonical=*/false);
+ TemplateArgument Arg(Lit, /*CanonKind=*/std::nullopt);
TemplateArgumentLocInfo ArgInfo(Lit);
ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo));
return BuildLiteralOperatorCall(R, OpNameInfo, {}, StringTokLocs.back(),
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 39c5e3b0671bb..485d4bc1becd9 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7415,6 +7415,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
case DeclSpec::TST_decltype: {
T = BuildDecltypeType(DS.getRepAsExpr(), /*AsUnevaluated=*/false);
DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
+ DecltypeTL.setUnderlyingExpr(DS.getRepAsExpr());
DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
break;
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index a4504410cae28..1a49b88162928 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1888,8 +1888,6 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
bool IsKnownInstance, const Scope *S) {
assert(!R.empty() && !R.isAmbiguous());
- SourceLocation loc = R.getNameLoc();
-
// If this is known to be an instance access, go ahead and build an
// implicit 'this' expression now.
QualType ThisTy = getCurrentThisType();
@@ -1900,7 +1898,7 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
SourceLocation Loc = R.getNameLoc();
if (SS.getRange().isValid())
Loc = SS.getRange().getBegin();
- baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true);
+ baseExpr = BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/true);
}
return BuildMemberReferenceExpr(
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index b96065f8619d2..994d5812b80af 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -3751,7 +3751,8 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
SFINAETrap Trap(*this);
CheckTemplateArgumentInfo CTAI;
TemplateArgumentLoc Arg(
- TemplateArgument(StringLit, /*IsCanonical=*/false), StringLit);
+ TemplateArgument(StringLit, /*CanonKind=*/std::nullopt),
+ StringLit);
if (CheckTemplateArgument(
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
/*ArgumentPackIndex=*/0, CTAI, CTAK_Specified) ||
diff --git a/clang/lib/Sema/SemaOpenACCAtomic.cpp b/clang/lib/Sema/SemaOpenACCAtomic.cpp
index ad21129d30c15..f381b96d1678d 100644
--- a/clang/lib/Sema/SemaOpenACCAtomic.cpp
+++ b/clang/lib/Sema/SemaOpenACCAtomic.cpp
@@ -424,9 +424,9 @@ class AtomicOperandChecker {
llvm::FoldingSetNodeID LHS_ID, InnerLHS_ID, InnerRHS_ID;
AssignInf.LHS->Profile(LHS_ID, SemaRef.getASTContext(),
- /*Canonical=*/true);
+ CanonicalizationKind::Structural);
BinInf->LHS->Profile(InnerLHS_ID, SemaRef.getASTContext(),
- /*Canonical=*/true);
+ CanonicalizationKind::Structural);
// This is X = X binop expr;
// Check the RHS is an expression.
@@ -440,7 +440,7 @@ class AtomicOperandChecker {
IDACInfo::AssignBinOp, AssignInf.LHS};
BinInf->RHS->Profile(InnerRHS_ID, SemaRef.getASTContext(),
- /*Canonical=*/true);
+ CanonicalizationKind::Structural);
// This is X = expr binop X;
// Check the LHS is an expression
if (LHS_ID == InnerRHS_ID)
@@ -581,11 +581,7 @@ class AtomicOperandChecker {
bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX,
IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) {
- llvm::FoldingSetNodeID First_ID, Second_ID;
- FirstX->Profile(First_ID, SemaRef.getASTContext(), /*Canonical=*/true);
- SecondX->Profile(Second_ID, SemaRef.getASTContext(), /*Canonical=*/true);
-
- if (First_ID == Second_ID)
+ if (SemaRef.getASTContext().hasSameExpr(FirstX, SecondX))
return false;
PartialDiagnostic PD =
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index ec9b2e68ef6cf..304669d978d25 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3513,13 +3513,14 @@ getAllocatorKind(Sema &S, DSAStackTy *Stack, Expr *Allocator) {
auto AllocatorKindRes = OMPAllocateDeclAttr::OMPUserDefinedMemAlloc;
llvm::FoldingSetNodeID AEId;
const Expr *AE = Allocator->IgnoreParenImpCasts();
- AE->IgnoreImpCasts()->Profile(AEId, S.getASTContext(), /*Canonical=*/true);
+ AE->IgnoreImpCasts()->Profile(AEId, S.getASTContext(),
+ CanonicalizationKind::Structural);
for (int I = 0; I < OMPAllocateDeclAttr::OMPUserDefinedMemAlloc; ++I) {
auto AllocatorKind = static_cast<OMPAllocateDeclAttr::AllocatorTypeTy>(I);
const Expr *DefAllocator = Stack->getAllocator(AllocatorKind);
llvm::FoldingSetNodeID DAEId;
DefAllocator->IgnoreImpCasts()->Profile(DAEId, S.getASTContext(),
- /*Canonical=*/true);
+ CanonicalizationKind::Structural);
if (AEId == DAEId) {
AllocatorKindRes = AllocatorKind;
break;
@@ -3541,12 +3542,8 @@ static bool checkPreviousOMPAllocateAttribute(
if (AllocatorsMatch &&
AllocatorKind == OMPAllocateDeclAttr::OMPUserDefinedMemAlloc &&
Allocator && PrevAllocator) {
- const Expr *AE = Allocator->IgnoreParenImpCasts();
- const Expr *PAE = PrevAllocator->IgnoreParenImpCasts();
- llvm::FoldingSetNodeID AEId, PAEId;
- AE->Profile(AEId, S.Context, /*Canonical=*/true);
- PAE->Profile(PAEId, S.Context, /*Canonical=*/true);
- AllocatorsMatch = AEId == PAEId;
+ AllocatorsMatch = S.Context.hasSameExpr(
+ Allocator->IgnoreParenImpCasts(), PrevAllocator->IgnoreParenImpCasts());
}
if (!AllocatorsMatch) {
SmallString<256> AllocatorBuffer;
@@ -11789,11 +11786,11 @@ bool OpenMPAtomicUpdateChecker::checkBinaryOperation(
Expr *RHS = AtomicInnerBinOp->getRHS();
llvm::FoldingSetNodeID XId, LHSId, RHSId;
X->IgnoreParenImpCasts()->Profile(XId, SemaRef.getASTContext(),
- /*Canonical=*/true);
+ CanonicalizationKind::Structural);
LHS->IgnoreParenImpCasts()->Profile(LHSId, SemaRef.getASTContext(),
- /*Canonical=*/true);
+ CanonicalizationKind::Structural);
RHS->IgnoreParenImpCasts()->Profile(RHSId, SemaRef.getASTContext(),
- /*Canonical=*/true);
+ CanonicalizationKind::Structural);
if (XId == LHSId) {
E = RHS;
IsXLHSInRHSPart = true;
@@ -11935,7 +11932,8 @@ bool OpenMPAtomicUpdateChecker::checkStatement(Stmt *S, unsigned DiagId,
/// Get the node id of the fixed point of an expression \a S.
llvm::FoldingSetNodeID getNodeId(ASTContext &Context, const Expr *S) {
llvm::FoldingSetNodeID Id;
- S->IgnoreParenImpCasts()->Profile(Id, Context, true);
+ S->IgnoreParenImpCasts()->Profile(Id, Context,
+ CanonicalizationKind::Structural);
return Id;
}
@@ -13162,8 +13160,10 @@ StmtResult SemaOpenMP::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
// Check that the first expression has form v = x.
Expr *PossibleX = BinOp->getRHS()->IgnoreParenImpCasts();
llvm::FoldingSetNodeID XId, PossibleXId;
- Checker.getX()->Profile(XId, Context, /*Canonical=*/true);
- PossibleX->Profile(PossibleXId, Context, /*Canonical=*/true);
+ Checker.getX()->Profile(XId, Context,
+ CanonicalizationKind::Structural);
+ PossibleX->Profile(PossibleXId, Context,
+ CanonicalizationKind::Structural);
IsUpdateExprFound = XId == PossibleXId;
if (IsUpdateExprFound) {
V = BinOp->getLHS();
@@ -13193,8 +13193,10 @@ StmtResult SemaOpenMP::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
// Check that the second expression has form v = x.
Expr *PossibleX = BinOp->getRHS()->IgnoreParenImpCasts();
llvm::FoldingSetNodeID XId, PossibleXId;
- Checker.getX()->Profile(XId, Context, /*Canonical=*/true);
- PossibleX->Profile(PossibleXId, Context, /*Canonical=*/true);
+ Checker.getX()->Profile(XId, Context,
+ CanonicalizationKind::Structural);
+ PossibleX->Profile(PossibleXId, Context,
+ CanonicalizationKind::Structural);
IsUpdateExprFound = XId == PossibleXId;
if (IsUpdateExprFound) {
V = BinOp->getLHS();
@@ -13237,10 +13239,10 @@ StmtResult SemaOpenMP::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
Expr *PossibleXLHSInSecond =
SecondBinOp->getLHS()->IgnoreParenImpCasts();
llvm::FoldingSetNodeID X1Id, X2Id;
- PossibleXRHSInFirst->Profile(X1Id, Context,
- /*Canonical=*/true);
- PossibleXLHSInSecond->Profile(X2Id, Context,
- /*Canonical=*/true);
+ PossibleXRHSInFirst->Profile(
+ X1Id, Context, CanonicalizationKind::Structural);
+ PossibleXLHSInSecond->Profile(
+ X2Id, Context, CanonicalizationKind::Structural);
IsUpdateExprFound = X1Id == X2Id;
if (IsUpdateExprFound) {
V = FirstBinOp->getLHS();
@@ -21154,13 +21156,9 @@ static bool actOnOMPReductionKindClause(
(DeclareReductionRef.isUsable() && IsParentBOK) ||
(IsParentBOK && BOK != ParentBOK) || IsParentReductionOp) {
bool EmitError = true;
- if (IsParentReductionOp && DeclareReductionRef.isUsable()) {
- llvm::FoldingSetNodeID RedId, ParentRedId;
- ParentReductionOp->Profile(ParentRedId, Context, /*Canonical=*/true);
- DeclareReductionRef.get()->Profile(RedId, Context,
- /*Canonical=*/true);
- EmitError = RedId != ParentRedId;
- }
+ if (IsParentReductionOp && DeclareReductionRef.isUsable())
+ EmitError = !Context.hasSameExpr(ParentReductionOp,
+ DeclareReductionRef.get());
if (EmitError) {
S.Diag(ReductionId.getBeginLoc(),
diag::err_omp_reduction_identifier_mismatch)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 11e771bc240f1..52e7f3cd94e00 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1361,8 +1361,10 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
return true;
// Is the function New an overload of the function Old?
- QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
- QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
+ QualType OldQType = SemaRef.Context.getCanonicalType(
+ Old->getType(), CanonicalizationKind::Functional);
+ QualType NewQType = SemaRef.Context.getCanonicalType(
+ New->getType(), CanonicalizationKind::Functional);
// Compare the signatures (C++ 1.3.10) of the two functions to
// determine whether they are overloads. If we find any mismatch
@@ -1409,7 +1411,7 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
bool SameTemplateParameterList = SemaRef.TemplateParameterListsAreEqual(
NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
OldTemplate->getTemplateParameters(), false, Sema::TPL_TemplateMatch);
- bool SameReturnType = SemaRef.Context.hasSameType(
+ bool SameReturnType = SemaRef.Context.hasFunctionallyEquivalentType(
Old->getDeclaredReturnType(), New->getDeclaredReturnType());
// FIXME(GH58571): Match template parameter list even for non-constrained
// template heads. This currently ensures that the code prior to C++20 is
@@ -1605,10 +1607,7 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
NewI != NewE || OldI != OldE; ++NewI, ++OldI) {
if (NewI == NewE || OldI == OldE)
return true;
- llvm::FoldingSetNodeID NewID, OldID;
- NewI->getCond()->Profile(NewID, SemaRef.Context, true);
- OldI->getCond()->Profile(OldID, SemaRef.Context, true);
- if (NewID != OldID)
+ if (!SemaRef.Context.hasSameExpr(NewI->getCond(), OldI->getCond()))
return true;
}
@@ -3508,7 +3507,7 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
}
// No extra info for same types.
- if (Context.hasSameType(FromType, ToType)) {
+ if (Context.hasFunctionallyEquivalentType(FromType, ToType)) {
PDiag << ft_default;
return;
}
@@ -3538,8 +3537,8 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
}
// Handle different return type.
- if (!Context.hasSameType(FromFunction->getReturnType(),
- ToFunction->getReturnType())) {
+ if (!Context.hasFunctionallyEquivalentType(FromFunction->getReturnType(),
+ ToFunction->getReturnType())) {
PDiag << ft_return_type << ToFunction->getReturnType()
<< FromFunction->getReturnType();
return;
@@ -3583,7 +3582,10 @@ bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
QualType NewType =
Context.removePtrSizeAddrSpace((New.begin() + J)->getUnqualifiedType());
- if (!Context.hasSameType(OldType, NewType)) {
+ assert(Context.isFunctionallyCanonicalType(OldType));
+ assert(Context.isFunctionallyCanonicalType(NewType));
+
+ if (OldType != NewType) {
if (ArgPos)
*ArgPos = Idx;
return false;
@@ -3595,6 +3597,10 @@ bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
bool Sema::FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
const FunctionProtoType *NewType,
unsigned *ArgPos, bool Reversed) {
+ OldType = cast<FunctionProtoType>(Context.getCanonicalType(
+ QualType(OldType, 0), CanonicalizationKind::Functional));
+ NewType = cast<FunctionProtoType>(Context.getCanonicalType(
+ QualType(NewType, 0), CanonicalizationKind::Functional));
return FunctionParamTypesAreEqual(OldType->param_types(),
NewType->param_types(), ArgPos, Reversed);
}
@@ -3613,8 +3619,15 @@ bool Sema::FunctionNonObjectParamTypesAreEqual(const FunctionDecl *OldFunction,
unsigned NewIgnore =
unsigned(NewFunction->hasCXXExplicitFunctionObjectParameter());
- auto *OldPT = cast<FunctionProtoType>(OldFunction->getFunctionType());
- auto *NewPT = cast<FunctionProtoType>(NewFunction->getFunctionType());
+ QualType OldFunctionType =
+ Context.getCanonicalType(QualType(OldFunction->getFunctionType(), 0),
+ CanonicalizationKind::Functional);
+ QualType NewFunctionType =
+ Context.getCanonicalType(QualType(NewFunction->getFunctionType(), 0),
+ CanonicalizationKind::Functional);
+
+ auto *OldPT = cast<FunctionProtoType>(OldFunctionType);
+ auto *NewPT = cast<FunctionProtoType>(NewFunctionType);
return FunctionParamTypesAreEqual(OldPT->param_types().slice(OldIgnore),
NewPT->param_types().slice(NewIgnore),
@@ -10748,8 +10761,10 @@ static Comparison compareEnableIfAttrs(const Sema &S, const FunctionDecl *Cand1,
Cand1ID.clear();
Cand2ID.clear();
- (*Cand1A)->getCond()->Profile(Cand1ID, S.getASTContext(), true);
- (*Cand2A)->getCond()->Profile(Cand2ID, S.getASTContext(), true);
+ (*Cand1A)->getCond()->Profile(Cand1ID, S.getASTContext(),
+ CanonicalizationKind::Structural);
+ (*Cand2A)->getCond()->Profile(Cand2ID, S.getASTContext(),
+ CanonicalizationKind::Structural);
if (Cand1ID != Cand2ID)
return Comparison::Worse;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 16b4815be2010..a96645420015e 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -957,7 +957,8 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef,
case ParsedTemplateArgument::NonType: {
Expr *E = Arg.getAsExpr();
- return TemplateArgumentLoc(TemplateArgument(E, /*IsCanonical=*/false), E);
+ return TemplateArgumentLoc(TemplateArgument(E, /*CanonKind=*/std::nullopt),
+ E);
}
case ParsedTemplateArgument::Template: {
@@ -1654,7 +1655,7 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
Param->setDefaultArgument(
Context, getTrivialTemplateArgumentLoc(
- TemplateArgument(Default, /*IsCanonical=*/false),
+ TemplateArgument(Default, /*CanonKind=*/std::nullopt),
QualType(), SourceLocation()));
}
@@ -3561,7 +3562,13 @@ static QualType checkBuiltinTemplateIdType(
// Synthesize a new template argument list, removing duplicates.
for (auto T : Ts.getPackAsArray()) {
assert(T.getKind() == clang::TemplateArgument::Type);
- if (!Seen.insert(T.getAsType().getCanonicalType()).second)
+ // FIXME: BTK__builtin_dedup_pack is not considered an alias template, so
+ // the template specialization cannot store a non-canonical underlying
+ // type.
+ // When that is fixed, this can use getCommonSugar so it doesn't preserve
+ // arbitrary sugar.
+ T = Context.getCanonicalTemplateArgument(T);
+ if (!Seen.insert(T.getAsType()).second)
continue;
OutArgs.push_back(T);
}
@@ -4172,8 +4179,7 @@ static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
case TemplateArgument::Type: {
QualType Type = Arg.getAsType();
- const TemplateTypeParmType *TPT =
- Arg.getAsType()->getAsCanonical<TemplateTypeParmType>();
+ const auto *TPT = dyn_cast<TemplateTypeParmType>(Type);
return TPT && !Type.hasQualifiers() &&
TPT->getDepth() == Depth && TPT->getIndex() == Index;
}
@@ -4196,9 +4202,12 @@ static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
llvm_unreachable("unexpected kind of template argument");
}
-static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
+static bool isSameAsPrimaryTemplate(Sema &S, TemplateParameterList *Params,
TemplateParameterList *SpecParams,
ArrayRef<TemplateArgument> Args) {
+ if (SpecParams->hasAssociatedConstraints())
+ return false;
+
if (Params->size() != Args.size() || Params->size() != SpecParams->size())
return false;
@@ -4225,8 +4234,20 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
if (auto *SpecNTTP =
dyn_cast<NonTypeTemplateParmDecl>(SpecParams->getParam(I))) {
auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(I));
- if (!NTTP || NTTP->getType().getCanonicalType() !=
- SpecNTTP->getType().getCanonicalType())
+ if (!NTTP || !S.Context.hasFunctionallyEquivalentType(
+ NTTP->getType(), SpecNTTP->getType()))
+ return false;
+ }
+
+ // For TTPs, further specialization is allowed by the template template
+ // parameter's template-head.
+ if (auto *SpecTTP =
+ dyn_cast<TemplateTemplateParmDecl>(SpecParams->getParam(I))) {
+ auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Params->getParam(I));
+ if (!TTP ||
+ !S.TemplateParameterListsAreEqual(
+ TTP->getTemplateParameters(), SpecTTP->getTemplateParameters(),
+ /*Complain=*/false, Sema::TPL_TemplateParamsEquivalent))
return false;
}
}
@@ -4407,28 +4428,34 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
/*UpdateArgsWithConversions=*/true))
return true;
+ // Only needed for partial specializations.
+ SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted;
+
// Find the variable template (partial) specialization declaration that
// corresponds to these arguments.
if (IsPartialSpecialization) {
if (CheckTemplatePartialSpecializationArgs(TemplateNameLoc, VarTemplate,
TemplateArgs.size(),
- CTAI.CanonicalConverted))
+ CTAI.SugaredConverted))
return true;
// FIXME: Move these checks to CheckTemplatePartialSpecializationArgs so
// we also do them during instantiation.
if (!Name.isDependent() &&
!TemplateSpecializationType::anyDependentTemplateArguments(
- TemplateArgs, CTAI.CanonicalConverted)) {
+ TemplateArgs, CTAI.SugaredConverted)) {
Diag(TemplateNameLoc, diag::err_partial_spec_fully_specialized)
<< VarTemplate->getDeclName();
IsPartialSpecialization = false;
}
- if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(),
- TemplateParams, CTAI.CanonicalConverted) &&
- (!Context.getLangOpts().CPlusPlus20 ||
- !TemplateParams->hasAssociatedConstraints())) {
+ FunctionallyEquivalentConverted = CTAI.SugaredConverted;
+ Context.canonicalizeTemplateArguments(FunctionallyEquivalentConverted,
+ CanonicalizationKind::Functional);
+
+ if (isSameAsPrimaryTemplate(*this, VarTemplate->getTemplateParameters(),
+ TemplateParams,
+ FunctionallyEquivalentConverted)) {
// C++ [temp.class.spec]p9b3:
//
// -- The argument list of the specialization shall not be identical
@@ -4448,7 +4475,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
if (IsPartialSpecialization)
PrevDecl = VarTemplate->findPartialSpecialization(
- CTAI.CanonicalConverted, TemplateParams, InsertPos);
+ FunctionallyEquivalentConverted, TemplateParams, InsertPos);
else
PrevDecl =
VarTemplate->findSpecialization(CTAI.CanonicalConverted, InsertPos);
@@ -4478,7 +4505,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
VarTemplatePartialSpecializationDecl::Create(
Context, VarTemplate->getDeclContext(), TemplateKWLoc,
TemplateNameLoc, TemplateParams, VarTemplate, TSI->getType(), TSI,
- SC, CTAI.CanonicalConverted);
+ SC, FunctionallyEquivalentConverted);
Partial->setTemplateArgsAsWritten(TemplateArgs);
if (!PrevPartial)
@@ -4895,9 +4922,9 @@ ExprResult Sema::CheckConceptTemplateId(
DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc());
// There's a bug with CTAI.CanonicalConverted.
- // If the template argument contains a DependentDecltypeType that includes a
+ // If the template argument contains a DecltypeType that includes a
// TypeAliasType, and the same written type had occurred previously in the
- // source, then the DependentDecltypeType would be canonicalized to that
+ // source, then the DecltypeType would be canonicalized to that
// previous type which would mess up the substitution.
// FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly!
auto *CSD = ImplicitConceptSpecializationDecl::Create(
@@ -5608,7 +5635,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc,
// If the resulting expression is new, then use it in place of the
// old expression in the template argument.
if (R != E) {
- TemplateArgument TA(R, /*IsCanonical=*/false);
+ TemplateArgument TA(R, /*CanonKind=*/std::nullopt);
ArgLoc = TemplateArgumentLoc(TA, R);
}
break;
@@ -6855,7 +6882,7 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction(
// Stop checking the precise nature of the argument if it is value dependent,
// it should be checked when instantiated.
if (Arg->isValueDependent()) {
- SugaredConverted = TemplateArgument(ArgIn, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(ArgIn, /*CanonKind=*/std::nullopt);
CanonicalConverted =
S.Context.getCanonicalTemplateArgument(SugaredConverted);
return false;
@@ -7044,7 +7071,7 @@ static bool CheckTemplateArgumentPointerToMember(
if (VD->getType()->isMemberPointerType()) {
if (isa<NonTypeTemplateParmDecl>(VD)) {
if (Arg->isTypeDependent() || Arg->isValueDependent()) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
S.Context.getCanonicalTemplateArgument(SugaredConverted);
} else {
@@ -7110,7 +7137,7 @@ static bool CheckTemplateArgumentPointerToMember(
// Okay: this is the address of a non-static member, and therefore
// a member pointer constant.
if (Arg->isTypeDependent() || Arg->isValueDependent()) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
S.Context.getCanonicalTemplateArgument(SugaredConverted);
} else {
@@ -7235,7 +7262,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
return ExprError();
setDeductionArg(E.get());
}
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted = TemplateArgument(
Context.getCanonicalTemplateArgument(SugaredConverted));
return Arg;
@@ -7266,7 +7293,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
// normal template rules apply: we accept the template if it would be valid
// for any number of expansions (i.e. none).
if (ArgPE && !StrictCheck) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted = TemplateArgument(
Context.getCanonicalTemplateArgument(SugaredConverted));
return Arg;
@@ -7292,7 +7319,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
return Arg;
}
if (isa<NonTypeTemplateParmDecl>(ND)) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
Context.getCanonicalTemplateArgument(SugaredConverted);
return Arg;
@@ -7349,7 +7376,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
// permitted (and expected) to be unable to determine a value.
if (ArgResult.get()->isValueDependent()) {
setDeductionArg(ArgResult.get());
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
Context.getCanonicalTemplateArgument(SugaredConverted);
return Arg;
@@ -7387,7 +7414,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
Value.getLValuePath()[0].getAsArrayIndex() == 0 &&
!Value.isLValueOnePastTheEnd() && ParamType->isPointerType()) {
if (ArgPE) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
Context.getCanonicalTemplateArgument(SugaredConverted);
} else {
@@ -7418,7 +7445,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff);
if (ArgPE) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
Context.getCanonicalTemplateArgument(SugaredConverted);
} else {
@@ -7466,7 +7493,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
// We can't check arbitrary value-dependent arguments.
if (Arg->isValueDependent()) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
Context.getCanonicalTemplateArgument(SugaredConverted);
return Arg;
@@ -7555,7 +7582,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
if (Arg->isValueDependent()) {
// The argument is value-dependent. Create a new
// TemplateArgument with the converted expression.
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
Context.getCanonicalTemplateArgument(SugaredConverted);
return Arg;
@@ -7723,7 +7750,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
// Deal with parameters of type std::nullptr_t.
if (ParamType->isNullPtrType()) {
if (Arg->isTypeDependent() || Arg->isValueDependent()) {
- SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+ SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
CanonicalConverted =
Context.getCanonicalTemplateArgument(SugaredConverted);
return Arg;
@@ -8273,7 +8300,7 @@ static bool MatchTemplateParameterKind(
// placeholder types
QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
- if (!S.Context.hasSameType(OldType, NewType)) {
+ if (!S.Context.hasFunctionallyEquivalentType(OldType, NewType)) {
if (Complain) {
unsigned NextDiag = diag::err_template_nontype_parm_different_type;
if (TemplateArgLoc.isValid()) {
@@ -8918,14 +8945,14 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
if (isPartialSpecialization) {
if (CheckTemplatePartialSpecializationArgs(TemplateNameLoc, ClassTemplate,
TemplateArgs.size(),
- CTAI.CanonicalConverted))
+ CTAI.SugaredConverted))
return true;
// FIXME: Move this to CheckTemplatePartialSpecializationArgs so we
// also do it during instantiation.
if (!Name.isDependent() &&
!TemplateSpecializationType::anyDependentTemplateArguments(
- TemplateArgs, CTAI.CanonicalConverted)) {
+ TemplateArgs, CTAI.SugaredConverted)) {
Diag(TemplateNameLoc, diag::err_partial_spec_fully_specialized)
<< ClassTemplate->getDeclName();
isPartialSpecialization = false;
@@ -8933,15 +8960,23 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
}
}
+ // Only needed for partial specializations.
+ SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted;
+
void *InsertPos = nullptr;
ClassTemplateSpecializationDecl *PrevDecl = nullptr;
- if (isPartialSpecialization)
+ if (isPartialSpecialization) {
+ FunctionallyEquivalentConverted = CTAI.SugaredConverted;
+ Context.canonicalizeTemplateArguments(FunctionallyEquivalentConverted,
+ CanonicalizationKind::Functional);
+
PrevDecl = ClassTemplate->findPartialSpecialization(
- CTAI.CanonicalConverted, TemplateParams, InsertPos);
- else
+ FunctionallyEquivalentConverted, TemplateParams, InsertPos);
+ } else {
PrevDecl =
ClassTemplate->findSpecialization(CTAI.CanonicalConverted, InsertPos);
+ }
ClassTemplateSpecializationDecl *Specialization = nullptr;
@@ -8969,16 +9004,9 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
if (!PrevDecl)
ClassTemplate->AddSpecialization(Specialization, InsertPos);
} else {
- CanQualType CanonType = CanQualType::CreateUnsafe(
- Context.getCanonicalTemplateSpecializationType(
- ElaboratedTypeKeyword::None,
- TemplateName(ClassTemplate->getCanonicalDecl()),
- CTAI.CanonicalConverted));
- if (Context.hasSameType(
- CanonType,
- ClassTemplate->getCanonicalInjectedSpecializationType(Context)) &&
- (!Context.getLangOpts().CPlusPlus20 ||
- !TemplateParams->hasAssociatedConstraints())) {
+ if (isSameAsPrimaryTemplate(*this, ClassTemplate->getTemplateParameters(),
+ TemplateParams,
+ FunctionallyEquivalentConverted)) {
// C++ [temp.class.spec]p9b3:
//
// -- The argument list of the specialization shall not be identical
@@ -9003,7 +9031,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
ClassTemplatePartialSpecializationDecl *Partial =
ClassTemplatePartialSpecializationDecl::Create(
Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
- ClassTemplate, CTAI.CanonicalConverted, CanonType, PrevPartial);
+ ClassTemplate, FunctionallyEquivalentConverted, PrevPartial);
Partial->setTemplateArgsAsWritten(TemplateArgs);
SetNestedNameSpecifier(*this, Partial, SS);
if (TemplateParameterLists.size() > 1 && SS.isSet()) {
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c71c40526ccdc..90b4c68c6e7e5 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -368,11 +368,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
if (Y.getKind() != TemplateArgument::Expression)
return checkDeducedTemplateArguments(Context, Y, X);
- // Compare the expressions for equality
- llvm::FoldingSetNodeID ID1, ID2;
- X.getAsExpr()->Profile(ID1, Context, true);
- Y.getAsExpr()->Profile(ID2, Context, true);
- if (ID1 == ID2)
+ if (Context.hasSameExpr(X.getAsExpr(), Y.getAsExpr()))
return X.wasDeducedFromArrayBound() ? Y : X;
// Differing dependent expressions are incompatible.
@@ -550,8 +546,9 @@ DeduceNullPtrTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
: CK_NullToPointer)
.get();
return DeduceNonTypeTemplateArgument(
- S, TemplateParams, NTTP, TemplateArgument(Value, /*IsCanonical=*/false),
- Value->getType(), Info, PartialOrdering, Deduced, HasDeducedAnyParam);
+ S, TemplateParams, NTTP,
+ TemplateArgument(Value, /*CanonKind=*/std::nullopt), Value->getType(),
+ Info, PartialOrdering, Deduced, HasDeducedAnyParam);
}
/// Deduce the value of the given non-type template parameter
@@ -565,8 +562,9 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
bool *HasDeducedAnyParam) {
return DeduceNonTypeTemplateArgument(
- S, TemplateParams, NTTP, TemplateArgument(Value, /*IsCanonical=*/false),
- Value->getType(), Info, PartialOrdering, Deduced, HasDeducedAnyParam);
+ S, TemplateParams, NTTP,
+ TemplateArgument(Value, /*CanonKind=*/std::nullopt), Value->getType(),
+ Info, PartialOrdering, Deduced, HasDeducedAnyParam);
}
/// Deduce the value of the given non-type template parameter
@@ -2874,7 +2872,8 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc,
TemplateParam)
.getAs<Expr>();
- return TemplateArgumentLoc(TemplateArgument(E, /*IsCanonical=*/false), E);
+ return TemplateArgumentLoc(TemplateArgument(E, /*CanonKind=*/std::nullopt),
+ E);
}
case TemplateArgument::NullPtr: {
@@ -2889,7 +2888,8 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
case TemplateArgument::Integral:
case TemplateArgument::StructuralValue: {
Expr *E = BuildExpressionFromNonTypeTemplateArgument(Arg, Loc).get();
- return TemplateArgumentLoc(TemplateArgument(E, /*IsCanonical=*/false), E);
+ return TemplateArgumentLoc(TemplateArgument(E, /*CanonKind=*/std::nullopt),
+ E);
}
case TemplateArgument::Template:
@@ -6263,11 +6263,14 @@ struct TemplateArgumentListAreEqual {
for (unsigned I = 0, E = Args1.size(); I < E; ++I) {
// We use profile, instead of structural comparison of the arguments,
- // because canonicalization can't do the right thing for dependent
- // expressions.
+ // because canonicalization can't do the right thing for expressions.
llvm::FoldingSetNodeID IDA, IDB;
- Args1[I].Profile(IDA, Ctx);
- Args2[I].Profile(IDB, Ctx);
+ Ctx.getCanonicalTemplateArgument(Args1[I],
+ CanonicalizationKind::Functional)
+ .Profile(IDA, Ctx);
+ Ctx.getCanonicalTemplateArgument(Args2[I],
+ CanonicalizationKind::Functional)
+ .Profile(IDB, Ctx);
if (IDA != IDB)
return false;
}
@@ -6282,13 +6285,14 @@ struct TemplateArgumentListAreEqual {
for (unsigned I = 0, E = Args1.size(); I < E; ++I) {
// We use profile, instead of structural comparison of the arguments,
- // because canonicalization can't do the right thing for dependent
- // expressions.
+ // because canonicalization can't do the right thing for expressions.
llvm::FoldingSetNodeID IDA, IDB;
- Args1[I].Profile(IDA, Ctx);
- // Unlike the specialization arguments, the injected arguments are not
- // always canonical.
- Ctx.getCanonicalTemplateArgument(Args2[I]).Profile(IDB, Ctx);
+ Ctx.getCanonicalTemplateArgument(Args1[I],
+ CanonicalizationKind::Functional)
+ .Profile(IDA, Ctx);
+ Ctx.getCanonicalTemplateArgument(Args2[I],
+ CanonicalizationKind::Functional)
+ .Profile(IDB, Ctx);
if (IDA != IDB)
return false;
}
@@ -6436,11 +6440,17 @@ Sema::getMoreSpecializedPartialSpecialization(
assert(PS1->getSpecializedTemplate() == PS2->getSpecializedTemplate() &&
"the partial specializations being compared should specialize"
" the same template.");
+ SmallVector<TemplateArgument, 8> Ps1Args(PS1->getTemplateArgs().asArray());
+ Context.canonicalizeTemplateArguments(Ps1Args);
+
+ SmallVector<TemplateArgument, 8> Ps2Args(PS2->getTemplateArgs().asArray());
+ Context.canonicalizeTemplateArguments(Ps2Args);
+
TemplateName Name(PS1->getSpecializedTemplate()->getCanonicalDecl());
QualType PT1 = Context.getCanonicalTemplateSpecializationType(
- ElaboratedTypeKeyword::None, Name, PS1->getTemplateArgs().asArray());
+ ElaboratedTypeKeyword::None, Name, Ps1Args);
QualType PT2 = Context.getCanonicalTemplateSpecializationType(
- ElaboratedTypeKeyword::None, Name, PS2->getTemplateArgs().asArray());
+ ElaboratedTypeKeyword::None, Name, Ps2Args);
TemplateDeductionInfo Info(Loc);
return getMoreSpecialized(*this, PT1, PT2, PS1, PS2, Info);
@@ -6455,10 +6465,14 @@ bool Sema::isMoreSpecializedThanPrimary(
Primary->getInjectedTemplateArgs(Context));
Context.canonicalizeTemplateArguments(PrimaryCanonArgs);
+ SmallVector<TemplateArgument, 8> PartialCanonArgs(
+ Spec->getTemplateArgs().asArray());
+ Context.canonicalizeTemplateArguments(PartialCanonArgs);
+
QualType PrimaryT = Context.getCanonicalTemplateSpecializationType(
ElaboratedTypeKeyword::None, Name, PrimaryCanonArgs);
QualType PartialT = Context.getCanonicalTemplateSpecializationType(
- ElaboratedTypeKeyword::None, Name, Spec->getTemplateArgs().asArray());
+ ElaboratedTypeKeyword::None, Name, PartialCanonArgs);
VarTemplatePartialSpecializationDecl *MaybeSpec =
getMoreSpecialized(*this, PartialT, PrimaryT, Spec, Primary, Info);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 09c2482168ab7..82e0bb411560e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4915,14 +4915,19 @@ TemplateDeclInstantiator::InstantiateClassTemplatePartialSpecialization(
// Check these arguments are valid for a template partial specialization.
if (SemaRef.CheckTemplatePartialSpecializationArgs(
PartialSpec->getLocation(), ClassTemplate, InstTemplateArgs.size(),
- CTAI.CanonicalConverted))
+ CTAI.SugaredConverted))
return nullptr;
+ SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted =
+ CTAI.SugaredConverted;
+ SemaRef.Context.canonicalizeTemplateArguments(
+ FunctionallyEquivalentConverted, CanonicalizationKind::Functional);
+
// Figure out where to insert this class template partial specialization
// in the member template's set of class template partial specializations.
void *InsertPos = nullptr;
ClassTemplateSpecializationDecl *PrevDecl =
- ClassTemplate->findPartialSpecialization(CTAI.CanonicalConverted,
+ ClassTemplate->findPartialSpecialization(FunctionallyEquivalentConverted,
InstParams, InsertPos);
// Create the class template partial specialization declaration.
@@ -4930,8 +4935,7 @@ TemplateDeclInstantiator::InstantiateClassTemplatePartialSpecialization(
ClassTemplatePartialSpecializationDecl::Create(
SemaRef.Context, PartialSpec->getTagKind(), Owner,
PartialSpec->getBeginLoc(), PartialSpec->getLocation(), InstParams,
- ClassTemplate, CTAI.CanonicalConverted,
- /*CanonInjectedTST=*/CanQualType(),
+ ClassTemplate, FunctionallyEquivalentConverted,
/*PrevDecl=*/nullptr);
InstPartialSpec->setTemplateArgsAsWritten(InstTemplateArgs);
@@ -5024,14 +5028,19 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
// Check these arguments are valid for a template partial specialization.
if (SemaRef.CheckTemplatePartialSpecializationArgs(
PartialSpec->getLocation(), VarTemplate, InstTemplateArgs.size(),
- CTAI.CanonicalConverted))
+ CTAI.SugaredConverted))
return nullptr;
+ SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted =
+ CTAI.SugaredConverted;
+ SemaRef.Context.canonicalizeTemplateArguments(
+ FunctionallyEquivalentConverted, CanonicalizationKind::Functional);
+
// Figure out where to insert this variable template partial specialization
// in the member template's set of variable template partial specializations.
void *InsertPos = nullptr;
VarTemplateSpecializationDecl *PrevDecl =
- VarTemplate->findPartialSpecialization(CTAI.CanonicalConverted,
+ VarTemplate->findPartialSpecialization(FunctionallyEquivalentConverted,
InstParams, InsertPos);
// Do substitution on the type of the declaration
@@ -5053,7 +5062,7 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
VarTemplatePartialSpecializationDecl::Create(
SemaRef.Context, Owner, PartialSpec->getInnerLocStart(),
PartialSpec->getLocation(), InstParams, VarTemplate, TSI->getType(),
- TSI, PartialSpec->getStorageClass(), CTAI.CanonicalConverted);
+ TSI, PartialSpec->getStorageClass(), FunctionallyEquivalentConverted);
InstPartialSpec->setTemplateArgsAsWritten(InstTemplateArgs);
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5b1aad3fa8470..797e535bc0957 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1412,13 +1412,18 @@ TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern(
}
case TemplateArgument::Expression: {
- PackExpansionExpr *Expansion
- = cast<PackExpansionExpr>(Argument.getAsExpr());
+ CanonicalizationKindOrNone CanonKind = std::nullopt;
+ Expr *OrigE = OrigLoc.getSourceExpression(), *E = OrigE;
+ if (!E) { // FIXME: Does this ever happen?
+ E = Argument.getAsExpr();
+ CanonKind = Argument.getExprCanonKind();
+ }
+ auto *Expansion = cast<PackExpansionExpr>(E);
Expr *Pattern = Expansion->getPattern();
- Ellipsis = Expansion->getEllipsisLoc();
+ if (OrigE)
+ Ellipsis = Expansion->getEllipsisLoc();
NumExpansions = Expansion->getNumExpansions();
- return TemplateArgumentLoc(
- TemplateArgument(Pattern, Argument.isCanonicalExpr()), Pattern);
+ return TemplateArgumentLoc(TemplateArgument(Pattern, CanonKind), Pattern);
}
case TemplateArgument::TemplateExpansion:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 3dc8ceb70f3cd..f7355144c0dd7 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -6071,6 +6071,7 @@ namespace {
}
void VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
assert(DS.getTypeSpecType() == DeclSpec::TST_decltype);
+ TL.setUnderlyingExpr(DS.getRepAsExpr());
TL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
TL.setRParenLoc(DS.getTypeofParensRange().getEnd());
}
@@ -10061,7 +10062,8 @@ QualType Sema::BuildDecltypeType(Expr *E, bool AsUnevaluated) {
// used to build SFINAE gadgets.
Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context);
}
- return Context.getDecltypeType(E, getDecltypeForExpr(E));
+ return Context.getDecltypeType(E, /*ExprCanonKind=*/std::nullopt,
+ getDecltypeForExpr(E));
}
QualType Sema::ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 40187f71231bd..d01da7d3d4fd3 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4078,7 +4078,7 @@ class TreeTransform {
return TemplateArgumentLoc();
return TemplateArgumentLoc(TemplateArgument(Result.get(),
- /*IsCanonical=*/false),
+ /*CanonKind=*/std::nullopt),
Result.get());
}
@@ -5108,7 +5108,7 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
if (E.isInvalid())
return true;
Output = TemplateArgumentLoc(
- TemplateArgument(E.get(), /*IsCanonical=*/false), E.get());
+ TemplateArgument(E.get(), /*CanonKind=*/std::nullopt), E.get());
return false;
}
}
@@ -7056,17 +7056,15 @@ QualType TreeTransform<Derived>::TransformTypeOfType(TypeLocBuilder &TLB,
return Result;
}
-template<typename Derived>
+template <typename Derived>
QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
DecltypeTypeLoc TL) {
- const DecltypeType *T = TL.getTypePtr();
-
// decltype expressions are not potentially evaluated contexts
EnterExpressionEvaluationContext Unevaluated(
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated, nullptr,
Sema::ExpressionEvaluationContextRecord::EK_Decltype);
- ExprResult E = getDerived().TransformExpr(T->getUnderlyingExpr());
+ ExprResult E = getDerived().TransformExpr(TL.getUnderlyingExpr());
if (E.isInvalid())
return QualType();
@@ -7075,15 +7073,15 @@ QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
return QualType();
QualType Result = TL.getType();
- if (getDerived().AlwaysRebuild() ||
- E.get() != T->getUnderlyingExpr()) {
+ if (getDerived().AlwaysRebuild() || E.get() != TL.getUnderlyingExpr()) {
Result = getDerived().RebuildDecltypeType(E.get(), TL.getDecltypeLoc());
if (Result.isNull())
return QualType();
- }
- else E.get();
+ } else
+ E.get();
DecltypeTypeLoc NewTL = TLB.push<DecltypeTypeLoc>(Result);
+ NewTL.setUnderlyingExpr(E.get());
NewTL.setDecltypeLoc(TL.getDecltypeLoc());
NewTL.setRParenLoc(TL.getRParenLoc());
return Result;
@@ -16533,7 +16531,7 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
ArgStorage = TemplateArgument(
new (getSema().Context)
PackExpansionExpr(DRE.get(), E->getPackLoc(), std::nullopt),
- /*IsCanonical=*/false);
+ /*CanonKind=*/std::nullopt);
}
PackArgs = ArgStorage;
}
@@ -16748,9 +16746,11 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
Replacement = E->getReplacement();
}
+ // FIXME: Track in SubstNonTypeTemplateParmExpr the canonical kind of the
+ // expression.
return getDerived().RebuildSubstNonTypeTemplateParmExpr(
AssociatedDecl, E->getParameter(), E->getNameLoc(),
- TemplateArgument(Replacement.get(), /*IsCanonical=*/false),
+ TemplateArgument(Replacement.get(), /*CanonKind=*/std::nullopt),
E->getPackIndex(), E->getFinal());
}
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 049dc227821cf..a042aaac745a5 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7590,6 +7590,7 @@ void TypeLocReader::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
}
void TypeLocReader::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
+ TL.setUnderlyingExpr(Reader.readExpr());
TL.setDecltypeLoc(readSourceLocation());
TL.setRParenLoc(readSourceLocation());
}
@@ -10246,12 +10247,12 @@ ASTRecordReader::readTemplateParameterList() {
}
void ASTRecordReader::readTemplateArgumentList(
- SmallVectorImpl<TemplateArgument> &TemplArgs,
- bool Canonicalize) {
+ SmallVectorImpl<TemplateArgument> &TemplArgs,
+ CanonicalizationKindOrNone CanonKind) {
unsigned NumTemplateArgs = readInt();
TemplArgs.reserve(NumTemplateArgs);
while (NumTemplateArgs--)
- TemplArgs.push_back(readTemplateArgument(Canonicalize));
+ TemplArgs.push_back(readTemplateArgument(CanonKind));
}
/// Read a UnresolvedSet structure.
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 9033ea55bc5e2..8fe5c473bb61c 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -946,7 +946,8 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
// Template arguments.
SmallVector<TemplateArgument, 8> TemplArgs;
- Record.readTemplateArgumentList(TemplArgs, /*Canonicalize*/ true);
+ Record.readTemplateArgumentList(TemplArgs,
+ CanonicalizationKind::Structural);
// Template args as written.
TemplateArgumentListInfo TemplArgsWritten;
@@ -2440,7 +2441,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
VisitDecl(D);
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < D->NumTemplateArgs; ++I)
- Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false));
+ Args.push_back(Record.readTemplateArgument());
D->setTemplateArguments(Args);
}
@@ -2543,7 +2544,7 @@ RedeclarableResult ASTDeclReader::VisitClassTemplateSpecializationDeclImpl(
}
SmallVector<TemplateArgument, 8> TemplArgs;
- Record.readTemplateArgumentList(TemplArgs, /*Canonicalize*/ true);
+ Record.readTemplateArgumentList(TemplArgs, CanonicalizationKind::Functional);
D->TemplateArgs = TemplateArgumentList::CreateCopy(C, TemplArgs);
D->PointOfInstantiation = readSourceLocation();
D->SpecializationKind = (TemplateSpecializationKind)Record.readInt();
@@ -2657,7 +2658,7 @@ RedeclarableResult ASTDeclReader::VisitVarTemplateSpecializationDeclImpl(
D->setTemplateArgsAsWritten(Record.readASTTemplateArgumentListInfo());
SmallVector<TemplateArgument, 8> TemplArgs;
- Record.readTemplateArgumentList(TemplArgs, /*Canonicalize*/ true);
+ Record.readTemplateArgumentList(TemplArgs, CanonicalizationKind::Functional);
D->TemplateArgs = TemplateArgumentList::CreateCopy(C, TemplArgs);
D->PointOfInstantiation = readSourceLocation();
D->SpecializationKind = (TemplateSpecializationKind)Record.readInt();
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 632137f01d767..8f2e061c0dca3 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -531,6 +531,7 @@ void TypeLocWriter::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
}
void TypeLocWriter::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
+ Record.AddStmt(TL.getUnderlyingExpr());
addSourceLocation(TL.getDecltypeLoc());
addSourceLocation(TL.getRParenLoc());
}
diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp
index 135191fae2e0b..c76a1fb9eb6bc 100644
--- a/clang/test/AST/ast-dump-templates.cpp
+++ b/clang/test/AST/ast-dump-templates.cpp
@@ -7565,7 +7565,7 @@ namespace TestAbbreviatedTemplateDecls {
// JSON-NEXT: {
// JSON-NEXT: "kind": "TemplateArgument",
// JSON-NEXT: "isExpr": true,
-// JSON-NEXT: "isCanonical": true,
+// JSON-NEXT: "CanonicalKind": "Structural",
// JSON-NEXT: "inner": [
// JSON-NEXT: {
// JSON-NEXT: "id": "0x{{.*}}",
@@ -7604,7 +7604,7 @@ namespace TestAbbreviatedTemplateDecls {
// JSON-NEXT: {
// JSON-NEXT: "kind": "TemplateArgument",
// JSON-NEXT: "isExpr": true,
-// JSON-NEXT: "isCanonical": true,
+// JSON-NEXT: "CanonicalKind": "Structural",
// JSON-NEXT: "inner": [
// JSON-NEXT: {
// JSON-NEXT: "id": "0x{{.*}}",
@@ -7864,7 +7864,7 @@ namespace TestAbbreviatedTemplateDecls {
// JSON-NEXT: {
// JSON-NEXT: "kind": "TemplateArgument",
// JSON-NEXT: "isExpr": true,
-// JSON-NEXT: "isCanonical": true,
+// JSON-NEXT: "CanonicalKind": "Structural",
// JSON-NEXT: "inner": [
// JSON-NEXT: {
// JSON-NEXT: "id": "0x{{.*}}",
@@ -7903,7 +7903,7 @@ namespace TestAbbreviatedTemplateDecls {
// JSON-NEXT: {
// JSON-NEXT: "kind": "TemplateArgument",
// JSON-NEXT: "isExpr": true,
-// JSON-NEXT: "isCanonical": true,
+// JSON-NEXT: "CanonicalKind": "Structural",
// JSON-NEXT: "inner": [
// JSON-NEXT: {
// JSON-NEXT: "id": "0x{{.*}}",
diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 8162f9caa8f15..f7dcafd3585cb 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -295,6 +295,14 @@ namespace cwg1968 { // cwg1968: no
#endif
} // namespace cwg1968
+namespace cwg1980 { // cwg1980: no drafting 2014-10-01
+#if __cplusplus >= 201103L
+ template<typename T, typename U> using X = T;
+ template<typename T> X<void, typename T::type> f() {}
+ template<typename T> X<void, typename T::other> f() {}
+#endif
+} // namespace cwg1980
+
namespace cwg1991 { // cwg1991: 3.9
#if __cplusplus >= 201103L
struct A {
diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp
index 75b4094283db0..5bb00e9f623f8 100644
--- a/clang/test/CXX/drs/cwg20xx.cpp
+++ b/clang/test/CXX/drs/cwg20xx.cpp
@@ -64,6 +64,19 @@ namespace cwg2061 { // cwg2061: 2.7
#endif // C++11
} // namespace cwg2061
+namespace cwg2064 { // cwg2064: 23
+#if __cplusplus >= 201103L
+ template<typename T> struct X {
+ template<typename U> struct Y {};
+ };
+ template<typename T> void g() {
+ X<decltype(sizeof(T))>::Y<int> y; // ok
+ return X<decltype(sizeof(T))>::f();
+ // expected-error at -1 {{no member named 'f' in 'cwg2064::X<unsigned long>'}}
+ }
+#endif
+}
+
namespace cwg2076 { // cwg2076: 13
#if __cplusplus >= 201103L
namespace std_example {
diff --git a/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp b/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp
index 2d46502e1d9b3..e885b69838021 100644
--- a/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp
@@ -8,10 +8,9 @@ template<class T> struct A {
};
B<short> b;
-template<typename T> using U = int;
+template<typename T> using U = int; // expected-note {{previous definition}}
template<typename ...T> void f(U<T> ...xs);
void g() { f<void,void,void>(1, 2, 3); }
-// FIXME: This is illegal, but probably only because CWG1044 missed this paragraph.
-template<typename T> using U = U<T>;
+template<typename T> using U = U<T>; // expected-error {{redefinition with different types}}
diff --git a/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
index 65d8345ecc3aa..a0c9e931c3298 100644
--- a/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
@@ -92,7 +92,7 @@ template X0::operator B<0>() const; // expected-error {{undefined function templ
// 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>'}}
+template X0::operator D<int, 0>() const; // expected-error {{undefined function template 'operator D<int, value-parameter-0-0>'}}
#endif
void test_X0(X0 x0, const X0 &x0c) {
diff --git a/clang/test/CodeGenCXX/mangle-exception-spec.cpp b/clang/test/CodeGenCXX/mangle-exception-spec.cpp
index 15f7a8b6cb504..2fdb383bb8cf2 100644
--- a/clang/test/CodeGenCXX/mangle-exception-spec.cpp
+++ b/clang/test/CodeGenCXX/mangle-exception-spec.cpp
@@ -35,15 +35,8 @@ template auto h<>(int()) -> int (*)();
// CHECK: define {{.*}} @_Z1hIJfEEPDwDpT_iEFivEPDwiS1_EFivE(
template auto h<float>(int()) -> int (*)();
-// FIXME: The C++11 manglings here are wrong; they should be the same as the
-// C++17 manglings.
-// The mangler mishandles substitutions for instantiation-dependent types that
-// differ only in type sugar that is not relevant for mangling. (In this case,
-// the types differ in presence/absence of ParenType nodes under the pointer.)
template<typename...T> auto i(int() throw(int, T...)) -> int (*)() throw(int, T...) { return nullptr; }
-// CHECK-CXX11: define {{.*}} @_Z1iIJEEPDwiDpT_EFivEPS2_(
-// CHECK-CXX17: define {{.*}} @_Z1iIJEEPDwiDpT_EFivES3_(
+// CHECK: define {{.*}} @_Z1iIJEEPDwiDpT_EFivES3_(
template auto i<>(int()) -> int (*)();
-// CHECK-CXX11: define {{.*}} @_Z1iIJfEEPDwiDpT_EFivEPS2_(
-// CHECK-CXX17: define {{.*}} @_Z1iIJfEEPDwiDpT_EFivES3_(
+// CHECK: define {{.*}} @_Z1iIJfEEPDwiDpT_EFivES3_(
template auto i<float>(int()) -> int (*)();
diff --git a/clang/test/CodeGenCXX/mangle-subst.cpp b/clang/test/CodeGenCXX/mangle-subst.cpp
index 524e0febe479a..e06c39b12809a 100644
--- a/clang/test/CodeGenCXX/mangle-subst.cpp
+++ b/clang/test/CodeGenCXX/mangle-subst.cpp
@@ -69,7 +69,7 @@ namespace NS {
namespace NS {
// CHECK: @_ZN2NS1fERNS_1CE
- void f(C&) { }
+ void f(C&) { }
}
namespace Test1 {
@@ -123,3 +123,11 @@ struct Inst : public A::Impl<A::Wrap> {};
void Test() { Inst a; }
}
+
+namespace InstantiationDependentDecltype {
+ struct a { a(char); };
+ struct b { a c(); };
+ // CHECK: @_ZN30InstantiationDependentDecltype1fINS_1bEEEvDTcvNS_1aEcldtcvT__E1cEES4_S3_S3_S2_S2_
+ template<typename d> void f(decltype(a(d().c())), decltype(a(d().c())), d, d, a, a);
+ void g(a a, b b) { f(a, a, b, b, a, a); }
+}
diff --git a/clang/test/CodeGenCXX/mangle-template.cpp b/clang/test/CodeGenCXX/mangle-template.cpp
index 2fca98faadb16..9da992d4fe445 100644
--- a/clang/test/CodeGenCXX/mangle-template.cpp
+++ b/clang/test/CodeGenCXX/mangle-template.cpp
@@ -383,9 +383,7 @@ namespace fixed_size_parameter_pack {
namespace type_qualifier {
template<typename T> using int_t = int;
template<typename T> void f(decltype(int_t<T*>() + 1)) {}
- // FIXME: This mangling doesn't work: we need to mangle the
- // instantiation-dependent 'int_t' operand.
- // CHECK: @_ZN14type_qualifier1fIPiEEvDTplcvi_ELi1EE
+ // CHECK: @_ZN14type_qualifier1fIPiEEvDTplcvNS_5int_tIPT_EE_ELi1EE
template void f<int*>(int);
// Note that this template has different constraints but would mangle the
@@ -395,9 +393,38 @@ namespace type_qualifier {
struct impl { using type = void; };
template<typename T> using alias = impl;
template<typename T> void g(decltype(alias<T*>::type(), 1)) {}
- // FIXME: Similarly we need to mangle the `T*` in here.
- // CHECK: @_ZN14type_qualifier1gIPiEEvDTcmcvv_ELi1EE
+ // CHECK: @_ZN14type_qualifier1gIPiEEvDTcmcvNS_5aliasIPT_ENS_4impl4typeEE_ELi1EE
template void g<int*>(int);
+
+ struct impl2 : private impl { using impl::type; };
+ template<typename T> using alias2 = impl2;
+ template<typename T> void h(decltype(alias2<T*>::type(), 1)) {}
+ // CHECK: @_ZN14type_qualifier1hIPiEEvDTcmcvNS_6alias2IPT_ENS_5impl24typeEE_ELi1EE
+ template void h<int*>(int);
+
+ struct impl3 {
+ template<class> using type = void;
+ };
+ template<typename T> using alias3 = impl3;
+ template<typename T> void i(decltype(alias3<T*>::type<char>(), 1)) {}
+ // CHECK: @_ZN14type_qualifier1iIPiEEvDTcmcvNS_6alias3IPT_ES_5impl34typeIcEE_ELi1EE
+ template void i<int*>(int);
+
+ struct impl4 : impl3 {
+ using impl3::type;
+ };
+ template<typename T> using alias4 = impl4;
+ template<typename T> void j(decltype(alias4<T*>::type<char>(), 1)) {}
+ // CHECK: @_ZN14type_qualifier1jIPiEEvDTcmcvNS_6alias4IPT_ES_5impl44typeIcEE_ELi1EE
+ template void j<int*>(int);
+
+ struct impl5 {
+ struct type {};
+ };
+ template<typename T> using alias5 = impl5;
+ template<typename T> void k(decltype(alias5<T*>::type(), 1)) {}
+ // CHECK: @_ZN14type_qualifier1kIPiEEvDTcmcvNS_6alias5IPT_ENS_5impl54typeEE_ELi1EE
+ template void k<int*>(int);
}
namespace unresolved_template_specialization_type {
diff --git a/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp b/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp
index 81d505588785a..0d1a8e6d06fb4 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp
@@ -49,7 +49,7 @@ void static_qux() {}
namespace PR31656 {
template <int I>
void __cdecl callee(int args[I]);
-// GCABI-LABEL: declare void @_ZN7PR316566calleeILi1EEEvPi(
+// GCABI-LABEL: declare void @_ZN7PR316566calleeILi1EEEvAT__i(
// MSABI: declare dso_local void @"??$callee@$00 at PR31656@@YAXQAH at Z"(
void caller() { callee<1>(0); }
diff --git a/clang/test/Sema/array-parameter.cpp b/clang/test/Sema/array-parameter.cpp
index 14cc88f2e36cb..dbf369a0941df 100644
--- a/clang/test/Sema/array-parameter.cpp
+++ b/clang/test/Sema/array-parameter.cpp
@@ -4,7 +4,7 @@ template <int N>
void func(int i[10]); // expected-note {{previously declared as 'int[10]' here}}
template <int N>
-void func(int i[N]); // expected-warning {{argument 'i' of type 'int[N]' with mismatched bound}}
+void func(int i[11]); // expected-warning {{argument 'i' of type 'int[11]' with mismatched bound}}
template <int N>
void func(int (&Val)[N]);
diff --git a/clang/test/Sema/invalid-bitwidth-expr.mm b/clang/test/Sema/invalid-bitwidth-expr.mm
index 9e577300eb1c8..25930e5d4ef7e 100644
--- a/clang/test/Sema/invalid-bitwidth-expr.mm
+++ b/clang/test/Sema/invalid-bitwidth-expr.mm
@@ -26,6 +26,7 @@ auto func() {
auto func() {
// error-bit should be propagated from TemplateArgument to NestNameSpecifier.
class Base<decltype(Foo(T()))>::type C; // expected-error {{no matching function for call to 'Foo'}}
+ // expected-error at -1 {{no class named 'type' in 'Base<bool>'}}
return C;
}
struct Z {
diff --git a/clang/test/SemaCXX/alias-template.cpp b/clang/test/SemaCXX/alias-template.cpp
index b49d36a6267e6..57d8e1a93ea83 100644
--- a/clang/test/SemaCXX/alias-template.cpp
+++ b/clang/test/SemaCXX/alias-template.cpp
@@ -2,8 +2,8 @@
namespace RedeclAliasTypedef {
template<typename U> using T = int;
- template<typename U> using T = int;
- template<typename U> using T = T<U>;
+ template<typename U> using T = int; // expected-note {{previous definition}}
+ template<typename U> using T = T<U>; // expected-error {{redefinition with different types}}
}
namespace IllegalTypeIds {
diff --git a/clang/test/SemaCXX/decltype.cpp b/clang/test/SemaCXX/decltype.cpp
index 45a4c4cf1ac86..aa6fd35ee2994 100644
--- a/clang/test/SemaCXX/decltype.cpp
+++ b/clang/test/SemaCXX/decltype.cpp
@@ -46,8 +46,8 @@ namespace pr10154 {
template<typename T> struct S {};
template<typename T> auto f(T t) -> decltype(S<int>(t)) {
- using U = decltype(S<int>(t));
- using U = S<int>;
+ using U = decltype(S<int>(t)); // expected-note {{previous definition}}
+ using U = S<int>; // expected-error {{redefinition with different types ('S<...>' vs 'S<...>')}}
return S<int>(t);
}
@@ -135,7 +135,7 @@ namespace GH97646 {
template<bool B>
void f() {
decltype(B) x = false;
- !x;
+ !x; // expected-warning {{expression result unused}}
}
}
@@ -241,6 +241,15 @@ void test() { (void)C::XBitMask<0>; }
}
#endif
+namespace ValueDependent {
+ template<int V> void f() {
+ decltype(V) x = nullptr;
+ // expected-error at -1 {{cannot initialize a variable of type 'decltype(V)' (aka 'int') with an rvalue of type 'std::nullptr_t'}}
+ }
+ template<typename T> decltype(int(T())) g() {}
+ template<typename T> decltype(int(T(0))) g() {}
+} // namespace ValueDependent
+
template<typename>
class conditional {
};
diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp
index eaa6cb04c5d1c..13846553421ce 100644
--- a/clang/test/SemaCXX/source_location.cpp
+++ b/clang/test/SemaCXX/source_location.cpp
@@ -9,7 +9,9 @@
// RUN: %clang_cc1 -std=c++2b -fcxx-exceptions -DUSE_CONSTEVAL -DPAREN_INIT -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -fms-compatibility -verify %s
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify -fms-compatibility %s
+#ifndef MS
// expected-no-diagnostics
+#endif
#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42)
#define CURRENT_FROM_MACRO() SL::current()
@@ -1088,6 +1090,9 @@ namespace GH178324 {
using e = int;
};
void current(const char * = __builtin_FUNCSIG());
- template <class> void c() { decltype(a(current()))::e; }
+ template <class> void c() {
+ decltype(a(current()))::e;
+ // expected-warning at -1 {{declaration does not declare anything}}
+ }
} // namespace GH178324
#endif
diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp
index 4c704db195c54..949ab504c57bf 100644
--- a/clang/test/SemaCXX/sugar-common-types.cpp
+++ b/clang/test/SemaCXX/sugar-common-types.cpp
@@ -143,7 +143,7 @@ namespace GH67603 {
using C = B;
using D = B;
N t = 0 ? A<decltype(C())>() : A<decltype(D())>();
- // expected-error at -1 {{rvalue of type 'A<decltype(C())>' (aka 'long')}}
+ // expected-error at -1 {{rvalue of type 'A<decltype(type-parameter-0-0())>' (aka 'long')}}
}
template void h<int>();
} // namespace GH67603
@@ -266,3 +266,19 @@ namespace FunctionTypeExtInfo {
// expected-error at -1 {{lvalue of type 'void (*)(__attribute__((swift_async_context)) B *)'}}
} // namespace TypedefType
} // namespace FunctionTypeExtInfo
+
+namespace Decltype {
+ N t1 = 0 ? decltype((X1*){}){} : decltype((Y1*){}){};
+ // expected-error at -1 {{rvalue of type 'decltype((int *){})' (aka 'int *')}}
+
+ using R = X1*;
+ extern R a;
+ using D1 = decltype(a);
+
+ using R = Y1*;
+ extern R a;
+ using E1 = decltype(a);
+
+ N t2 = 0 ? D1{} : E1{};
+ // expected-error at -1 {{rvalue of type 'decltype(a)' (aka 'int *')}}
+} // namespace Decltype
diff --git a/clang/test/SemaCXX/typeof.cpp b/clang/test/SemaCXX/typeof.cpp
index 421cfc59f5311..e6d3dbccf7321 100644
--- a/clang/test/SemaCXX/typeof.cpp
+++ b/clang/test/SemaCXX/typeof.cpp
@@ -8,6 +8,6 @@ namespace GH97646 {
template<bool B>
void f() {
__typeof__(B) x = false;
- !x;
+ !x; // expected-warning {{expression result unused}}
}
}
diff --git a/clang/test/SemaTemplate/GH164330.cpp b/clang/test/SemaTemplate/GH164330.cpp
index 767420de272e6..b58fb69290622 100644
--- a/clang/test/SemaTemplate/GH164330.cpp
+++ b/clang/test/SemaTemplate/GH164330.cpp
@@ -162,7 +162,7 @@ template <typename T> struct PathFieldId {
};
template <PathFieldId...> constexpr auto PathImplHelper();
-template <int N> using FieldName = FieldName<N>;
+template <int N> using FieldName = FieldNameEnum::type;
enum class FieldNumber;
template <PathFieldId... fields>
constexpr auto Path = PathImplHelper<fields...>();
diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
index 9811b18f4301b..527a2aa575c91 100644
--- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
+++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
@@ -519,15 +519,15 @@ concept something_interesting = requires {
template <class T>
struct X { // #defined-here
- void foo() requires requires { requires is_not_same_v<T, int>; };
- void bar(decltype(requires { requires is_not_same_v<T, int>; }));
+ void foo() requires requires { requires is_not_same_v<T, int>; }; // #foo-decl
+ void bar(decltype(requires { requires is_not_same_v<T, int>; })); // #bar-decl
};
template <class T>
void X<T>::foo() requires requires { requires something_interesting<T>; } {}
// expected-error at -1{{definition of 'foo' does not match any declaration}}
// expected-note@#defined-here{{defined here}}
-// expected-note at -8{{member declaration nearly matches}}
+// expected-note@#foo-decl{{member declaration nearly matches}}
template <class T>
void X<T>::foo() requires requires { requires is_not_same_v<T, int>; } {} // ok
@@ -536,6 +536,7 @@ template <class T>
void X<T>::bar(decltype(requires { requires something_interesting<T>; })) {}
// expected-error at -1{{definition of 'bar' does not match any declaration}}
// expected-note@#defined-here{{defined here}}
+// expected-note@#bar-decl{{member declaration nearly matches}}
template <class T>
void X<T>::bar(decltype(requires { requires is_not_same_v<T, int>; })) {}
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index ac80d16b4ccf8..7a3bf419eab68 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1824,6 +1824,16 @@ namespace GH176402 {
recursiveLambda(recursiveLambda, 5);
}
}
+
+namespace GH61818 {
+ template <typename T> concept C = true;
+ template <typename T> struct A;
+ template <> struct A<bool> { using type = bool; };
+
+ template <typename T>
+ void f(A<decltype(C<T>)>::type); // OK, no 'typename' needed
+} // namespace GH61818
+
namespace GH191016 {
template <typename T = int>
struct S {
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index 9e5756ffec3fc..29693e1918263 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -854,17 +854,17 @@ CC c{};
// CHECK-LABEL: Dumping GH133132::<deduction guide for CC>:
// CHECK-NEXT: FunctionTemplateDecl {{.+}} implicit <deduction guide for CC>
-// CHECK-NEXT: |-NonTypeTemplateParmDecl {{.+}} 'int' depth 0 index 0 N
-// CHECK-NEXT: | `-TemplateArgument {{.+}} expr '42'
-// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 42
-// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} class depth 0 index 1 U
-// CHECK-NEXT: | `-TemplateArgument type 'A<decltype(N)>'
-// CHECK-NEXT: | `-TemplateSpecializationType {{.+}} 'A<decltype(N)>' dependent
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} class depth 0 index 0 U
+// CHECK-NEXT: | `-TemplateArgument type 'A<decltype(N)>':'GH133132::A<int>'
+// CHECK-NEXT: | `-TemplateSpecializationType {{.+}} 'A<decltype(N)>' sugar instantiation_dependent
// CHECK-NEXT: | |-name: 'A':'GH133132::A' qualified
// CHECK-NEXT: | | `-ClassTemplateDecl {{.+}} A
-// CHECK-NEXT: | `-TemplateArgument type 'decltype(N)'
-// CHECK-NEXT: | `-DecltypeType {{.+}} 'decltype(N)' dependent
-// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' NonTypeTemplateParm {{.+}} 'N' 'int'
+// CHECK-NEXT: | |-TemplateArgument type 'decltype(N)':'int'
+// CHECK-NEXT: | | `-DecltypeType {{.+}} 'decltype(N)' sugar instantiation_dependent
+// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'int' NonTypeTemplateParm {{.+}} 'N' 'int'
+// CHECK-NEXT: | | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT: | `-RecordType {{.+}} 'GH133132::A<int>' canonical
+// CHECK-NEXT: | `-ClassTemplateSpecialization {{.+}} 'A'
// CHECK-NEXT: |-TypeTraitExpr {{.+}} 'bool' __is_deducible
// CHECK-NEXT: | |-DeducedTemplateSpecializationType {{.+}} 'GH133132::CC' dependent
// CHECK-NEXT: | | `-name: 'GH133132::CC'
@@ -872,14 +872,13 @@ CC c{};
// CHECK-NEXT: | `-TemplateSpecializationType {{.+}} 'GH133132::A<U>' dependent
// CHECK-NEXT: | |-name: 'GH133132::A'
// CHECK-NEXT: | | `-ClassTemplateDecl {{.+}} A
-// CHECK-NEXT: | `-TemplateArgument type 'U':'type-parameter-0-1'
+// CHECK-NEXT: | `-TemplateArgument type 'U':'type-parameter-0-0'
// CHECK-NEXT: | `-SubstTemplateTypeParmType {{.+}} 'U' sugar dependent class depth 0 index 0 _Ty
// CHECK-NEXT: | |-FunctionTemplate {{.+}} '<deduction guide for A>'
-// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
+// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 0
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'U'
// CHECK-NEXT: |-CXXDeductionGuideDecl {{.+}} implicit <deduction guide for CC> 'auto () -> GH133132::A<U>'
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} implicit used <deduction guide for CC> 'auto () -> GH133132::A<GH133132::A<int>>' implicit_instantiation
-// CHECK-NEXT: |-TemplateArgument integral '42'
// CHECK-NEXT: `-TemplateArgument type 'GH133132::A<int>'
// CHECK-NEXT: `-RecordType {{.+}} 'GH133132::A<int>'
// CHECK-NEXT: `-ClassTemplateSpecialization {{.+}} 'A'
diff --git a/clang/test/SemaTemplate/dependent-expr.cpp b/clang/test/SemaTemplate/dependent-expr.cpp
index ce210d9b74f6d..9b9980dd00b16 100644
--- a/clang/test/SemaTemplate/dependent-expr.cpp
+++ b/clang/test/SemaTemplate/dependent-expr.cpp
@@ -129,7 +129,7 @@ namespace PR45083 {
template<typename> void f() {
decltype(({})) x; // expected-error {{incomplete type}}
}
- template void f<int>(); // expected-note {{instantiation of}}
+ template void f<int>();
template<typename> auto g() {
auto c = [](auto, int) -> decltype(({})) {};
diff --git a/clang/test/SemaTemplate/dependent-type-identity.cpp b/clang/test/SemaTemplate/dependent-type-identity.cpp
index c826268c864c4..8d2791b790293 100644
--- a/clang/test/SemaTemplate/dependent-type-identity.cpp
+++ b/clang/test/SemaTemplate/dependent-type-identity.cpp
@@ -148,9 +148,7 @@ namespace PR21289 {
void g() { f<void, void, void>(); }
template<typename ...Ts> void h(S<int>) {}
- // Pending a core issue, it's not clear if these are redeclarations, but they
- // are probably intended to be... even though substitution can succeed for one
- // of them but fail for the other!
- template<typename ...Ts> void h(S<X<Ts>...>) {} // expected-note {{previous}}
- template<typename ...Ts> void h(S<Y<Ts, sizeof(Ts)>...>) {} // expected-error {{redefinition}}
+
+ template<typename ...Ts> void h(S<X<Ts>...>) {}
+ template<typename ...Ts> void h(S<Y<Ts, sizeof(Ts)>...>) {}
}
diff --git a/clang/test/SemaTemplate/injected-class-name.cpp b/clang/test/SemaTemplate/injected-class-name.cpp
index 93a7231b8c7b7..e1a0573393d11 100644
--- a/clang/test/SemaTemplate/injected-class-name.cpp
+++ b/clang/test/SemaTemplate/injected-class-name.cpp
@@ -69,3 +69,47 @@ namespace ConflictingRedecl {
template<typename> struct Nested; // expected-error {{member 'Nested' has the same name as its class}}
};
}
+
+namespace TwoLevels {
+ template <class> struct A {
+ template <bool> A f();
+ };
+ template <class T> template <bool> A<T> A<T>::f() {}
+} // namespace TwoLevels
+
+namespace TwoLevelsAlias1 {
+ template <class> struct A;
+ template <class T> using alias = T;
+ template <class T> struct B {
+ using type = alias<T>;
+ template <class> A<type> f();
+ };
+ template <class T> template <class>
+ A<typename B<T>::type> B<T>::f() {}
+} // namespace TwoLevelsAlias
+
+namespace TwoLevelsAlias2 {
+ template <class> struct A;
+ template <class T> using alias = typename A<T>::type;
+ template <class T> struct B {
+ template <class> typename A<T>::type f();
+ };
+ template <class T> template <class> alias<T> B<T>::f() {}
+} // namespace TwoLevelsAlias2
+
+namespace TwoLevelsAlias3 {
+ template <class T> using void_t = void;
+ template <class T> struct A { // expected-note {{defined here}}
+ template <int> void_t<typename T::type> f();
+ };
+ template <class T> template <int> void A<T>::f() {} // expected-error {{does not match}}
+} // namspace TwoLevelsAlias3
+
+namespace Unique {
+ template <class T> struct A {
+ template <class> A<T> f1();
+ template <class> A<T> f2();
+ };
+ template <class T> template <class> A<T> A<T>::f1() {}
+ template <class T> template <class> A<T> A<T>::f2() {}
+} // namespace Unique
diff --git a/clang/test/SemaTemplate/instantiate-expr-1.cpp b/clang/test/SemaTemplate/instantiate-expr-1.cpp
index 20d3edd86f391..b4c79c0c1cf29 100644
--- a/clang/test/SemaTemplate/instantiate-expr-1.cpp
+++ b/clang/test/SemaTemplate/instantiate-expr-1.cpp
@@ -206,3 +206,13 @@ void foo() {
void test() { foo<void>(); }
} // namespace TestAsmCleanup
+
+namespace GH190495 {
+ template <bool> struct __all;
+ template <class> bool __is_nothrow_swappable_v;
+ template <class _Tp> struct __tuple_impl {
+ void swap() noexcept(__all<__is_nothrow_swappable_v<_Tp>>::value);
+ void swap() const
+ noexcept(__all<__is_nothrow_swappable_v<const _Tp>>::value);
+ };
+} // namespace GH190495
diff --git a/clang/test/SemaTemplate/instantiation-dependence.cpp b/clang/test/SemaTemplate/instantiation-dependence.cpp
index 1a97dbf769ec6..b8096383e8c37 100644
--- a/clang/test/SemaTemplate/instantiation-dependence.cpp
+++ b/clang/test/SemaTemplate/instantiation-dependence.cpp
@@ -1,8 +1,9 @@
-// RUN: %clang_cc1 -std=c++23 -verify %s
+// RUN: %clang_cc1 -std=c++26 -verify %s
// Ensure we substitute into instantiation-dependent but non-dependent
// constructs. The poster-child for this is...
template<class ...> using void_t = void;
+template<class T, class> using alias = T;
namespace PR24076 {
template<class T> T declval();
@@ -48,17 +49,13 @@ namespace PR46791 { // also PR45782
static constexpr int specialization = 0;
};
- // FIXME: Per a strict interpretation of the C++ rules, the two void_t<...>
- // types below are equivalent -- we only (effectively) do token-by-token
- // comparison for *expressions* appearing within types. But all other
- // implementations accept this, using rules that are unclear.
template<typename T>
- struct trait<T, void_t<typename T::value_type>> { // expected-note {{previous}} FIXME-note {{matches}}
+ struct trait<T, void_t<typename T::value_type>> { // expected-note {{matches}}
static constexpr int specialization = 1;
};
template<typename T>
- struct trait<T, void_t<typename T::element_type>> { // expected-error {{redefinition}} FIXME-note {{matches}}
+ struct trait<T, void_t<typename T::element_type>> { // expected-note {{matches}}
static constexpr int specialization = 2;
};
@@ -68,11 +65,9 @@ namespace PR46791 { // also PR45782
struct D : B, C {};
static_assert(trait<A>::specialization == 0);
- static_assert(trait<B>::specialization == 1); // FIXME expected-error {{failed}} \
- // expected-note {{evaluates to '0 == 1'}}
- static_assert(trait<C>::specialization == 2); // FIXME expected-error {{failed}} \
- // expected-note {{evaluates to '0 == 2'}}
- static_assert(trait<D>::specialization == 0); // FIXME-error {{ambiguous partial specialization}}
+ static_assert(trait<B>::specialization == 1);
+ static_assert(trait<C>::specialization == 2);
+ static_assert(trait<D>::specialization == 0); // expected-error {{ambiguous partial specialization}}
}
namespace TypeQualifier {
@@ -103,3 +98,308 @@ namespace MemberOfInstantiationDependentBase {
void q(C1<int> *c) { c->f(0); }
void q(C2<int> *c) { c->f(0); }
}
+
+namespace GH8740 {
+ struct A { typedef int T; };
+ template<int> struct U { typedef int T; };
+ template<typename> struct S {
+ A a;
+ int n = decltype(a)::T();
+ int m = U<sizeof(a)>::T();
+ };
+ S<char> s;
+} // namespace GH8740
+
+namespace NonInstDependentArgs1 {
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, void_t<char>> {}; // expected-note {{previous}}
+ template<class T> struct X<T, void_t<void>> {}; // expected-error {{redefinition}}
+
+ template<class T, class = void> bool x;
+ template<class T> bool x<T, void_t<char>>; // expected-note {{previous}}
+ template<class T> bool x<T, void_t<void>>; // expected-error {{redefinition}}
+} // namespace NonInstDependentArgs1
+
+namespace NonInstDependentArgs2 {
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, void_t<T, void>> {};
+ template<class T> struct X<T, void_t<T, char>> {};
+
+ template<class T, class = void> bool x;
+ template<class T> bool x<T, void_t<T, char>>;
+ template<class T> bool x<T, void_t<T, void>>;
+} // namespace NonInstDependentArgs2
+
+namespace Level1 {
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, void_t<T>> {};
+ template<class T> struct X<T, void_t<T*>> {};
+} // namespace Level1
+
+namespace Level2 {
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, void_t<void_t<T>>> {};
+ template<class T> struct X<T, void_t<void_t<T*>>> {};
+} // namespace Level2
+
+namespace IndirectAlias1 {
+ template<class T> using alias2 = void_t<T>;
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, void_t<T>> {}; // expected-note {{previous}}
+ template<class T> struct X<T, alias2<T>> {}; // expected-error {{redefinition}}
+} // namspace IndirectAlias1
+
+namespace IndirectAlias2 {
+ template<class T, class U> using alias2 = alias<T, U>;
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, T> {};
+ template<class T> struct X<T, alias<T, T>> {}; // expected-note {{previous}}
+ template<class T> struct X<T, alias2<T, T>> {}; // expected-error {{redefinition}}
+} // namespace IndirectAlias2
+
+namespace PackIndexing1 {
+ // FIXME: This should not be a redefinition.
+ template<class ...Ts> using aliaspack = Ts...[0];
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, T> {}; // expected-note {{previous}}
+ template<class T> struct X<T, aliaspack<T, typename T::type>> {}; // expected-error {{redefinition}}
+} // namespace PackIndexing1
+
+namespace DeclType1 {
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, decltype(void())> {}; // expected-note {{previous}}
+ template<class T> struct X<T, decltype(void_t<char>())> {}; // expected-error {{redefinition}}
+} // namespace DeclType1
+
+namespace DeclType2 {
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, decltype(void())> {};
+ template<class T> struct X<T, decltype(void_t<typename T::type>())> {}; // expected-note {{previous}}
+ template<class T> struct X<T, decltype(void_t<typename T::type>())> {}; // expected-error {{redefinition}}
+} // namespace DeclType2
+
+namespace DeclType3 {
+ template<class T, class = void> struct X;
+ template<class T> struct X<T, decltype(void())> {};
+ template<class T> struct X<T, decltype(void_t<typename T::type>())> {};
+ template<class T> struct X<T, decltype(void_t<typename T::bar>())> {};
+} // namespace DeclType3
+
+namespace DeclType4 {
+ template<class T> using X = decltype(void()); // expected-note {{previous}}
+ template<class T> using X = decltype(void_t<typename T::type>()); // expected-error {{redefinition with different types}}
+} // namespace DeclType4
+
+namespace DeclType5 {
+ template<class T> using X = decltype(void_t<typename T::bar>()); // expected-note {{previous}}
+ template<class T> using X = decltype(void_t<typename T::type>()); // expected-error {{redefinition with different types}}
+} // namespace DeclType5
+
+namespace TempArg1 {
+ template<void*> struct A;
+ template<class T> using X = A<(void*){}>; // expected-note {{previous}}
+ template<class T> using X = A<(void_t<typename T::type>*){}>; // expected-error {{redefinition with different types}}
+} // namespace TempArg1
+
+namespace TempArg2 {
+ template<void*> struct A;
+ template<class T> using X = A<(void_t<typename T::bar>*){}>; // expected-note {{previous}}
+ template<class T> using X = A<(void_t<typename T::type>*){}>; // expected-error {{redefinition with different types}}
+} // namespace TempArg2
+
+namespace ConstTempParam1 {
+ // FIXME: Create a disambiguation rule for this.
+ // Maybe: If one candidate has functional types in the signature, the
+ // other does not, the former is more specialized.
+ template<class T, T N> struct Test0; // expected-note {{declared here}}
+ template<class T, alias<T, typename T::type> N>
+ struct Test0<T, N>;
+ // expected-error at -1 {{not more specialized than the primary template}}
+} // namespace ConstTempParam1
+
+namespace NestedClassSpec1 {
+ template<class T> struct A {
+ template<class> struct Cls;
+ template<class U> struct Cls<alias<U, typename U::type>> {};
+ template<class U> struct Cls<alias<U, typename U::bar>> {};
+ };
+} // nestedClassSpec1
+
+namespace NestedClassSpec2 {
+ template<class T> struct A {
+ template<class> struct Cls;
+ template<class U> struct Cls<alias<U, void>> {};
+ // expected-error at -1 {{does not specialize any template argument}}
+ template<class U> struct Cls<alias<U, typename U::type>> {}; // expected-note {{previous definition is here}}
+ template<class U> struct Cls<alias<U, typename U::type>> {};
+ // expected-error at -1 {{redefinition}}
+ };
+} // nestedClassSpec2
+
+namespace NestedClassSpec3 {
+ template<template<class...> class TT> struct A {
+ template<class,class> struct Cls;
+ template<class T> struct Cls<int, TT<T, typename T::type>> {};
+ template<class T> struct Cls<int, TT<T, typename T::bar>> {};
+ };
+ template struct A<alias>;
+} // nestedClassSpec3
+
+namespace NestedVarSpec1 {
+ template<class T> struct A {
+ template<class> static int Var;
+ template<class U> static int Var<alias<U, typename U::type>>;
+ template<class U> static int Var<alias<U, typename U::bar>>;
+ };
+} // namespace NestedVarSpec1
+
+namespace NestedVarSpec2 {
+ template<class T> struct A {
+ template<class> static int Var;
+ template<class U> static int Var<alias<U, void>>;
+ // expected-error at -1 {{does not specialize any template argument}}
+ template<class U> static int Var<alias<U, typename U::type>>; // expected-note {{previous declaration is here}}
+ template<class U> static int Var<alias<U, typename U::type>>;
+ // expected-error at -1 {{duplicate member 'Var'}}
+ };
+} // namespace NestedVarSpec2
+
+namespace NestedVarSpec3 {
+ template<template<class...> class TT> struct A {
+ template<class,class> static int Var;
+ template<class T> static int Var<int, TT<T, typename T::type>>;
+ template<class T> static int Var<int, TT<T, typename T::bar>>;
+ };
+ template struct A<alias>;
+} // nestedClassSpec3
+
+namespace VarRedecl1 {
+ template<class T> extern void *x; // expected-note {{previous declaration}}
+ template<class T> extern void_t<T> *x;
+ // expected-error at -1 {{redeclaration of 'x' with a different type}}
+} // namespace VarRedecl1
+
+namespace VarRedecl2 {
+ template<class T> extern void_t<T> *x;
+ template<class T> extern void_t<T> *x;
+} // namespace VarRedecl2
+
+namespace VarRedecl3 {
+ template<class T> extern void_t<T, typename T::foo> *x; // expected-note {{previous declaration}}
+ template<class T> extern void_t<T, typename T::bar> *x;
+ // expected-error at -1 {{redeclaration of 'x' with a different type}}
+} // namespace VarRedecl3
+
+namespace FuncParamDecay1 {
+ template<int N> constexpr int f(char *) { return 1; }
+ template<int N> constexpr int f(char[N]) = delete;
+ static_assert(f<0>(nullptr) == 1);
+} // namespace FuncParamDecay1
+
+namespace ArrayQualifiers1 {
+ template<class T, class...> using alias = const int[5];
+ template<class T> void f() {
+ using X = const alias<T>;
+ using X = alias<T>;
+ };
+} // namespace ArrayQualifiers1
+
+namespace ArrayQualifiers2 {
+ template<class T, class...> using alias = const int;
+ template<class T> void f() {
+ using X = const alias<T>[5];
+ using X = alias<T>[5];
+ };
+} // namespace ArrayQualifiers2
+
+namespace TempTempParam1 {
+ template<class T, template<void*> class> void f() {}
+ template<class T, template<void_t<T>*> class> void f() {}
+
+ template<void*> struct A;
+
+ template<class, class> struct B;
+ template<class T, void* V> struct B<T, A<V>> {};
+ template<class T, void_t<T>* V> struct B<T, A<V>> {};
+
+ template<class,class> struct C;
+ template<class T, template<void*> class TT> struct C<T, TT<nullptr>> {};
+ template<class T, template<void_t<T>*> class TT> struct C<T, TT<nullptr>> {};
+} // namespace TempTempParam1
+
+namespace OverloadExpr1 {
+ template <class T> T &&declval();
+
+ int g(int);
+ template <class T> typename T::type g(T);
+
+ template <class T, class = void> struct X;
+
+ template <class T>
+ struct X<T, decltype((g(declval<T>()), void()))> {
+ static constexpr auto val = 1;
+ };
+
+ template <class T>
+ struct X<T, decltype((g<>(declval<T>()), void()))> {
+ static constexpr auto val = 2;
+ };
+
+ static_assert(X<int>::val == 1);
+} // namespace OverloadExpr1
+
+namespace NameQualifierUsing {
+ struct Base {
+ using type = int;
+ };
+ template <class T> struct S : Base {
+ using Base::type;
+
+ void f(S<alias<T, typename T::foo>>::type) {}
+ void f(S<alias<T, typename T::bar>>::type) {}
+ };
+} // namespace QualfierUsing
+
+namespace NameQualifierUnresolvedUsing1 {
+ template <class T> struct A : T {
+ using typename T::foo;
+ void f(foo) {}
+ void f(A<alias<T, typename T::bar>>::foo) {}
+ void f(A<alias<T, typename T::baz>>::foo) {}
+ };
+} // namespace NameQualifierUnresolvedUsing1
+
+namespace RequiresExpr1 {
+ template<class T>
+ requires (alias<T, typename T::foo>() == 0)
+ void f() {}
+
+ template<class T>
+ requires (alias<T, typename T::bar>() == 0)
+ void f() {}
+
+ template<typename T> requires (sizeof(alias<T, typename T::foo>) >= 4)
+ bool a = false; // expected-note{{template is declared here}}
+
+ template<typename T> requires (sizeof(alias<T, typename T::bar>) >= 4 && sizeof(T) <= 10)
+ bool a<T> = true; // expected-error{{variable template partial specialization is not more specialized than the primary template}}
+} // namespace RequiresExpr1
+
+namespace NoexceptExpr1 {
+ template<class T>
+ void f() noexcept(alias<T, typename T::foo>() == 0) {}
+ // expected-note at -1 {{previous declaration is here}}
+
+ template<class T>
+ void f() noexcept(alias<T, typename T::bar>() == 0) {}
+ // expected-error at -1 {{exception specification in declaration does not match previous declaration}}
+} // namespace NoexceptExpr1
+
+// FIXME: This is a flaky test
+#if 0
+namespace UnaryTransformDecay {
+ template<class T, class U = void> struct X;
+ template<class T> struct X<T, __decay(int[T()])> {}; // FIXME-note {{previous}}
+ template<class T> struct X<T, __decay(int[T()])> {}; // FIXME-error {{redefinition}}
+} // namespace UnaryTransformDecay
+#endif
diff --git a/clang/test/SemaTemplate/partial-spec-instantiate.cpp b/clang/test/SemaTemplate/partial-spec-instantiate.cpp
index 44b58008a1d33..ab48050938991 100644
--- a/clang/test/SemaTemplate/partial-spec-instantiate.cpp
+++ b/clang/test/SemaTemplate/partial-spec-instantiate.cpp
@@ -165,3 +165,10 @@ namespace GH162855 {
template struct C<D<int>>;
} // namespace GH162855
#endif
+
+namespace DependentWithQualifier {
+ template <class> struct A;
+ template <class> struct B;
+ template <class T> struct A<const B<T> > {};
+ template struct A<const B<int> >;
+} // namespace DependentWithQualifier
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
index 9c25e26f43c36..f9460d976174a 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
@@ -406,14 +406,14 @@ namespace PR42362 {
namespace QualConv {
int *X;
template<const int *const *P> void f() {
- using T = decltype(P);
- using T = const int* const*;
+ using T = decltype(P); // expected-note {{previous definition}}
+ using T = const int* const*; // expected-error {{redefinition with different types ('const int *const *' vs 'decltype(P)' (aka 'const int *const *'))}}
}
template void f<&X>();
template<const int *const &R> void g() {
- using T = decltype(R);
- using T = const int *const &;
+ using T = decltype(R); // expected-note {{previous definition}}
+ using T = const int *const &; // expected-error {{redefinition with different types ('const int *const &' vs 'decltype(R)' (aka 'const int *const &'))}}
}
template void g<(const int *const&)X>();
}
@@ -421,15 +421,17 @@ namespace QualConv {
namespace FunctionConversion {
struct a { void c(char *) noexcept; };
template<void (a::*f)(char*)> void g() {
- using T = decltype(f);
+ using T = decltype(f); // expected-note {{previous definition}}
using T = void (a::*)(char*); // (not 'noexcept')
+ // expected-error at -1 {{redefinition with different types ('void (a::*)(char *)' vs 'decltype(f)' (aka 'void (a::*)(char *)'))}}
}
template void g<&a::c>();
void c() noexcept;
template<void (*p)()> void h() {
- using T = decltype(p);
+ using T = decltype(p);// expected-note {{previous definition}}
using T = void (*)(); // (not 'noexcept')
+ // expected-error at -1 {{redefinition with different types ('void (*)()' vs 'decltype(p)' (aka 'void (*)()'))}}
}
template void h<&c>();
}
@@ -437,8 +439,8 @@ namespace FunctionConversion {
namespace VoidPtr {
// Note, this is an extension in C++17 but valid in C++20.
template<void *P> void f() {
- using T = decltype(P);
- using T = void*;
+ using T = decltype(P); // expected-note {{previous definition}}
+ using T = void*; // expected-error {{redefinition with different types ('void *' vs 'decltype(P)' (aka 'void *'))}}
}
int n;
template void f<(void*)&n>();
diff --git a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
index bde811c3bf685..1d97deeb9214a 100644
--- a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
@@ -135,8 +135,17 @@ namespace Auto {
int n;
template<auto A, decltype(A) B = &n> struct SubstFailure;
- TInt<SubstFailure> isf; // FIXME: this should be ill-formed
+ // expected-error at -1 {{value of type 'int *' is not implicitly convertible to 'decltype(value-parameter-0-0)' (aka 'int')}}
+ // expected-note@#TInt {{while checking a default template argument used here}}
+ TInt<SubstFailure> isf;
+ // expected-note at -1 {{template template argument has different template parameters than its corresponding template template parameter}}
TIntPtr<SubstFailure> ipsf;
+
+ template<template<auto A, auto B, decltype(A)> typename C> struct TAutoAutoFirst {};
+ template<auto A, auto B, decltype(A)> struct AutoAutoFirst;
+ template<auto A, auto B, decltype(B)> struct AutoAutoSecond;
+ TAutoAutoFirst<AutoAutoFirst> aaf;
+ TAutoAutoFirst<AutoAutoSecond> aas; // FIXME: this should be rejected due to parameter mismatch
}
namespace GH62529 {
diff --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp
index 5023b5e093ec3..7af9fc8f83d7a 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -254,7 +254,7 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
const int Result = 42;
auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
// Arg is instantiated with '40 + 2'
- TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);
+ TemplateArgument Arg(ConstExpr, /*CanonKind=*/std::nullopt);
// Param has default expr of '42'
auto const *Param = Params->getParam(1);
@@ -270,7 +270,7 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
// Arg is instantiated with '40 + 1'
- TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);
+ TemplateArgument Arg(ConstExpr, /*CanonKind=*/std::nullopt);
// Param has default expr of '42'
auto const *Param = Params->getParam(1);
@@ -286,7 +286,7 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
// Arg is instantiated with '4 + 0'
- TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);
+ TemplateArgument Arg(ConstExpr, /*CanonKind=*/std::nullopt);
// Param has is value-dependent expression (i.e., sizeof(T))
auto const *Param = Params->getParam(3);
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index 2b5c8f6e14048..4354561e9b01e 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -1022,7 +1022,8 @@ static QualType makeConstInt(clang::ASTContext &ctxt) {
TEST_F(TestTypeSystemClang, TestGetTypeClassDeclType) {
clang::ASTContext &ctxt = m_ast->getASTContext();
auto *nullptr_expr = new (ctxt) CXXNullPtrLiteralExpr(ctxt.NullPtrTy, SourceLocation());
- QualType t = ctxt.getDecltypeType(nullptr_expr, makeConstInt(ctxt));
+ QualType t = ctxt.getDecltypeType(
+ nullptr_expr, /*ExprCanonKind=*/std::nullopt, makeConstInt(ctxt));
EXPECT_EQ(lldb::eTypeClassBuiltin, m_ast->GetTypeClass(t.getAsOpaquePtr()));
}
More information about the cfe-commits
mailing list