[clang] [lldb] [clang] implement CWG1980 and CWG2064: instantiation-dependency improvements (PR #190495)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 7 08:06:25 PDT 2026
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/190495
>From a367d77b109ce97957b972b7ef38cd1e0373cd0b 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/docs/ReleaseNotes.md | 8 +-
clang/include/clang/AST/ASTContext.h | 99 +-
clang/include/clang/AST/DependenceFlags.h | 8 +-
clang/include/clang/AST/Expr.h | 10 +-
clang/include/clang/AST/PropertiesBase.td | 1 +
clang/include/clang/AST/Stmt.h | 10 +-
clang/include/clang/AST/TemplateBase.h | 11 +-
clang/include/clang/AST/TextNodeDumper.h | 1 +
clang/include/clang/AST/TypeBase.h | 47 +-
clang/include/clang/AST/TypeProperties.td | 5 +-
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/include/clang/Basic/Specifiers.h | 74 ++
clang/include/clang/Sema/Sema.h | 8 +-
clang/include/clang/Sema/Template.h | 6 +-
clang/lib/AST/ASTContext.cpp | 1047 ++++++++++++++---
clang/lib/AST/Decl.cpp | 14 +-
clang/lib/AST/DeclTemplate.cpp | 11 +-
clang/lib/AST/ItaniumMangle.cpp | 274 +++--
clang/lib/AST/ODRHash.cpp | 2 +
clang/lib/AST/StmtProfile.cpp | 66 +-
clang/lib/AST/TemplateBase.cpp | 10 +-
clang/lib/AST/Type.cpp | 119 +-
clang/lib/CodeGen/CGExprCXX.cpp | 5 +-
clang/lib/Sema/HLSLExternalSemaSource.cpp | 9 +-
clang/lib/Sema/SemaCXXScopeSpec.cpp | 198 ++--
clang/lib/Sema/SemaConcept.cpp | 13 +-
clang/lib/Sema/SemaDecl.cpp | 44 +-
clang/lib/Sema/SemaDeclCXX.cpp | 4 +-
clang/lib/Sema/SemaExceptionSpec.cpp | 9 +-
clang/lib/Sema/SemaExprCXX.cpp | 10 +-
clang/lib/Sema/SemaExprMember.cpp | 4 +-
clang/lib/Sema/SemaOpenACCAtomic.cpp | 6 +-
clang/lib/Sema/SemaOpenMP.cpp | 18 +-
clang/lib/Sema/SemaOverload.cpp | 45 +-
clang/lib/Sema/SemaTemplate.cpp | 115 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 46 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 63 +-
clang/lib/Sema/SemaTemplateVariadic.cpp | 7 +-
clang/lib/Sema/SemaType.cpp | 3 +
clang/lib/Sema/TreeTransform.h | 5 +-
clang/test/AST/ast-dump-templates-pattern.cpp | 27 +-
clang/test/AST/ast-dump-templates.cpp | 8 +-
clang/test/CXX/drs/cwg19xx.cpp | 8 +
clang/test/CXX/drs/cwg20xx.cpp | 13 +
clang/test/CXX/drs/cwg31xx.cpp | 29 +-
.../CXX/temp/temp.decls/temp.alias/p3.cpp | 5 +-
.../test/CXX/temp/temp.decls/temp.mem/p5.cpp | 2 +-
clang/test/CodeGenCXX/mangle-cxx2c.cpp | 6 +-
.../test/CodeGenCXX/mangle-exception-spec.cpp | 11 +-
clang/test/CodeGenCXX/mangle-subst.cpp | 10 +-
clang/test/CodeGenCXX/mangle-template.cpp | 46 +-
.../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 | 11 +-
.../test/SemaTemplate/injected-class-name.cpp | 44 +
.../test/SemaTemplate/instantiate-expr-1.cpp | 10 +
clang/test/SemaTemplate/instantiate-scope.cpp | 10 +-
.../SemaTemplate/instantiation-dependence.cpp | 340 +++++-
.../SemaTemplate/temp_arg_nontype_cxx1z.cpp | 18 +-
.../SemaTemplate/temp_arg_template_p0522.cpp | 11 +-
clang/unittests/AST/TypePrinterTest.cpp | 6 +-
clang/www/cxx_dr_status.html | 14 +-
lldb/unittests/Symbol/TestTypeSystemClang.cpp | 3 +-
74 files changed, 2368 insertions(+), 796 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c7806878a4ff4..72b053026f1c7 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -269,6 +269,12 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Implemented [CWG1780 Explicit instantiation/specialization of generic lambda
operator()](https://cplusplus.github.io/CWG/issues/1780.html)
+- 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)
+- Clang now implements CWG3179: SFINAE tricks on void function parameters are
+ not allowed.
- Clang now allows omitting `typename` before a template name in a
conversion operator, implementing [CWG2413](https://wg21.link/cwg2413).
- Member specializations can now be declared in class scope, according to the
@@ -1104,7 +1110,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Fixed a crash in code completion when using a C-Style cast with a parenthesized
operand in Objective-C++ mode. (#GH180125)
-- Fixed a crash when code completion is triggered inside an ill-formed lambda's trailing requires-clause. (#GH201632)
+- Fixed a crash when code completion is triggered inside an ill-formed lambda's trailing requires-clause. (#GH201632)
### Static Analyzer
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index c04b380f9ec5b..96f778720474f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -399,6 +399,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// current file before they are compared locally.
unsigned NextStringLiteralVersion = 0;
+ /// A cache mapping from types to their functional canonical type.
+ mutable llvm::DenseMap<const Type *, QualType> FunctionalCanonicalTypeCache;
+
/// MD5 hash of CUID. It is calculated when first used and cached by this
/// data member.
mutable std::string CUIDHash;
@@ -1580,7 +1583,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);
@@ -2023,11 +2033,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
ElaboratedTypeKeyword Keyword, TemplateName T,
ArrayRef<TemplateArgument> CanonicalArgs) const;
+ /// \param Unique Whether to perform uniquing for this type. Generally
+ /// avoided, unless necessary for correctness, because it's very expensive.
QualType
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,
@@ -2100,7 +2113,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,
@@ -2988,7 +3002,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) {
@@ -2998,8 +3021,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 hasEquivalentType(QualType T1, QualType T2) const {
+ return hasSameType(T1, T2) &&
+ getCanonicalType(T1, CanonicalizationKind::Functional) ==
+ getCanonicalType(T2, CanonicalizationKind::Functional);
+ }
+ bool isFunctionalCanonicalType(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.
@@ -3084,6 +3126,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.
///
@@ -3102,8 +3155,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);
+ }
/// Return the default argument of a template parameter, if one exists.
const TemplateArgument *
@@ -3156,14 +3217,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 *
@@ -3306,6 +3381,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
// Helper for integer ordering
unsigned getIntegerRank(const Type *T) const;
+ SplitQualType buildFunctionalCanonicalType(const Type *T) const;
+
public:
//===--------------------------------------------------------------------===//
// Type Compatibility Predicates
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 b016126e9a813..36a63e3d76384 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -7487,12 +7487,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..4cfda71873383 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;
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index f07ba9205661b..fcd8aae098889 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1621,8 +1621,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..5d4723045f208 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -190,7 +190,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,
@@ -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 0c515556a8b84..9743d6cf2818f 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -335,6 +335,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 3a801e2857b13..4e388dabf64e6 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"
@@ -2144,6 +2145,16 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
unsigned AttrKind : 32 - NumTypeBits;
};
+ class DecltypeTypeBitfields {
+ friend class DecltypeType;
+
+ LLVM_PREFERRED_TYPE(TypeBitfields)
+ unsigned : NumTypeBits;
+
+ LLVM_PREFERRED_TYPE(CanonicalizationKindOrNone)
+ unsigned ExprCanonKind : 2;
+ };
+
class DeducedTypeBitfields {
friend class DeducedType;
@@ -2373,6 +2384,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
ArrayTypeBitfields ArrayTypeBits;
ConstantArrayTypeBitfields ConstantArrayTypeBits;
AttributedTypeBitfields AttributedTypeBits;
+ DecltypeTypeBitfields DecltypeTypeBits;
DeducedTypeBitfields DeducedTypeBits;
AutoTypeBitfields AutoTypeBits;
TypeOfBitfields TypeOfBits;
@@ -6356,44 +6368,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
@@ -7512,7 +7522,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/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index e2168d0a00ff4..3f8a0df13b413 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/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 38d9e4046d3a5..f96241a6de64f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5105,6 +5105,8 @@ def err_void_only_param : Error<
"'void' must be the first and only parameter if specified">;
def err_void_param_qualified : Error<
"'void' as parameter must not have type qualifiers">;
+def err_void_param_not_equivalent_to_void : Error<
+ "'void' as parameter must not involve template parameters">;
def err_void_explicit_object_param : Error<
"explicit object parameter cannot have 'void' type">;
def err_ident_list_in_fn_declaration : Error<
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 1fe50760eff9a..5dbe999cb289d 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -348,6 +348,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 631a680727fee..141d09154a251 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3280,7 +3280,11 @@ class Sema final : public SemaBase {
/// dependent and not the current instantiation).
DeclContext *computeDeclContext(const CXXScopeSpec &SS,
bool EnteringContext = false);
- bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
+
+ // FIXME: Make this a method of CXXScopeSpec.
+ static bool isDependentScopeSpecifier(const CXXScopeSpec &SS) {
+ return SS.getScopeRep().isDependent();
+ }
/// If the given nested name specifier refers to the current
/// instantiation, return the declaration that corresponds to that
@@ -10277,6 +10281,8 @@ 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 in functional canonical form
+ /// (CanonicalizationKind::Functional).
bool FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
ArrayRef<QualType> New,
unsigned *ArgPos = nullptr,
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index b0170c21feb1a..076a5ebe296bb 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -724,10 +724,8 @@ enum class TemplateSubstitutionKind : char {
TemplateParameterList *
SubstTemplateParams(TemplateParameterList *List);
- bool SubstQualifier(const DeclaratorDecl *OldDecl,
- DeclaratorDecl *NewDecl);
- bool SubstQualifier(const TagDecl *OldDecl,
- TagDecl *NewDecl);
+ void SubstQualifier(const DeclaratorDecl *OldDecl, DeclaratorDecl *NewDecl);
+ void SubstQualifier(const TagDecl *OldDecl, TagDecl *NewDecl);
VarTemplateSpecializationDecl *VisitVarTemplateSpecializationDecl(
VarTemplateDecl *VarTemplate, VarDecl *FromVar,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5f4da42c18a79..e238e7fece16a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3251,21 +3251,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;
}
//===----------------------------------------------------------------------===//
@@ -5000,16 +5012,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;
@@ -5049,6 +5062,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 {
@@ -5112,48 +5186,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.
@@ -6124,8 +6161,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);
@@ -6146,9 +6183,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);
@@ -6170,18 +6225,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() +
@@ -6190,6 +6243,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);
}
@@ -6773,34 +6828,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);
}
@@ -7118,24 +7174,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,
@@ -7401,12 +7482,10 @@ ASTContext::getDefaultTemplateArgumentOrNone(const NamedDecl *P) const {
}
}
-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();
@@ -7428,7 +7507,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});
@@ -7438,23 +7518,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();
@@ -7475,17 +7557,51 @@ 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;
+ }
+ if (!AnyChanged)
+ return Name;
+ return getDeducedTemplateName(
+ CanonUnderlying,
+ /*DefaultArgs=*/{DefArgs.StartPos, CanonArgs});
+ }
+ 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!");
@@ -7965,51 +8081,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);
@@ -8167,7 +8303,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,
@@ -13970,14 +14110,554 @@ 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 = FunctionalCanonicalTypeCache.find(T);
+ if (It != FunctionalCanonicalTypeCache.end())
+ ST = It->second.split();
+ else {
+ ST = buildFunctionalCanonicalType(T);
+ QualType R = getQualifiedType(ST);
+
+ assert(hasSameType(QualType(T, 0), R));
+
+ auto [_, Inserted] = FunctionalCanonicalTypeCache.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::buildFunctionalCanonicalType(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;
}
@@ -14354,6 +15034,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) {
@@ -14623,9 +15326,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);
@@ -14801,9 +15512,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/Decl.cpp b/clang/lib/AST/Decl.cpp
index 835f5f946bb7f..2f54a28f68760 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1250,6 +1250,13 @@ getExplicitVisibilityAux(const NamedDecl *ND,
return getVisibilityOf(InstantiatedFrom, kind);
}
+ // Use the most recent declaration.
+ if (!IsMostRecent && !isa<NamespaceDecl>(ND)) {
+ const NamedDecl *MostRecent = ND->getMostRecentDecl();
+ if (MostRecent != ND)
+ return getExplicitVisibilityAux(MostRecent, kind, true);
+ }
+
// If there wasn't explicit visibility there, and this is a
// specialization of a class template, check for visibility
// on the pattern.
@@ -1266,13 +1273,6 @@ getExplicitVisibilityAux(const NamedDecl *ND,
return std::nullopt;
}
- // Use the most recent declaration.
- if (!IsMostRecent && !isa<NamespaceDecl>(ND)) {
- const NamedDecl *MostRecent = ND->getMostRecentDecl();
- if (MostRecent != ND)
- return getExplicitVisibilityAux(MostRecent, kind, true);
- }
-
if (const auto *Var = dyn_cast<VarDecl>(ND)) {
if (Var->isStaticDataMember()) {
VarDecl *InstantiatedFrom = Var->getInstantiatedFromStaticDataMember();
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 861974848d6b3..e127a08e8620a 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);
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e5cdd6f31c507..c96d5b1ed42c3 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -534,8 +534,11 @@ 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);
+ // Returns whether this started a nested name.
+ // The caller is responsible for finishing it with a 'E'.
+ [[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,
@@ -549,7 +552,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"
@@ -1314,7 +1316,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
@@ -2255,34 +2257,101 @@ 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: {
+ const TemplateDecl *TD = Template.getAsTemplateDecl();
+ const DeclContext *DC = Context.getEffectiveDeclContext(TD);
- DependentTemplateName *Dependent = Template.getAsDependentTemplateName();
- assert(Dependent && "unexpected template name kind");
+ if (DC->isTranslationUnit() || isStdNamespace(DC)) {
+ mangleUnscopedTemplateName(TD, DC);
+ return false;
+ }
+ if (!InPrefix)
+ Out << 'N';
+ mangleTemplatePrefix(TD);
+ return true;
+ }
+ case clang::TemplateName::UsingTemplate:
+ llvm_unreachable(
+ "unexpected underlying template name kind for qualified template");
+ case clang::TemplateName::QualifiedTemplate: {
+ if (!InPrefix)
+ Out << 'N';
- // 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;
+ if (mangleSubstitution(Template))
+ return true;
- manglePrefix(Dependent->getQualifier());
+ const QualifiedTemplateName *S = Template.getAsQualifiedTemplateName();
+ manglePrefix(S->getQualifier());
- 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");
+ }
+ mangleUnqualifiedName(ND, /*DC=*/nullptr);
+ addSubstitution(Template);
+ return true;
+ }
+ case clang::TemplateName::DependentTemplate: {
+ DependentTemplateName *Dependent = Template.getAsDependentTemplateName();
+ assert(Dependent && "unexpected template name kind");
- if (IdentifierOrOverloadedOperator Name = Dependent->getName();
- const IdentifierInfo *Id = Name.getIdentifier())
- mangleSourceName(Id);
- else
- mangleOperatorName(Name.getOperator(), UnknownArity);
+ if (!InPrefix)
+ Out << 'N';
- addSubstitution(Template);
+ // 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;
+
+ 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");
+ }
+ llvm_unreachable("unhandled template name kind");
+}
+
+void CXXNameMangler::mangleNameWithPrefix(GlobalDecl GD,
+ NestedNameSpecifier Qualifier) {
+ if (!Qualifier)
+ return mangleName(GD);
+
+ Out << 'N';
+ manglePrefix(Qualifier);
+ mangleUnqualifiedName(GD, /*DC=*/nullptr);
+ Out << 'E';
}
void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
@@ -2368,21 +2437,42 @@ void CXXNameMangler::mangleType(TemplateName TN) {
if (mangleSubstitution(TN))
return;
- TemplateDecl *TD = nullptr;
-
switch (TN.getKind()) {
- case TemplateName::QualifiedTemplate:
+ case TemplateName::QualifiedTemplate: {
+ const QualifiedTemplateName *S = TN.getAsQualifiedTemplateName();
+
+ Out << 'N';
+ manglePrefix(S->getQualifier());
+
+ 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");
+ }
+ mangleUnqualifiedName(ND, /*DC=*/nullptr);
+ Out << 'E';
+ break;
+ }
+
case TemplateName::UsingTemplate:
- case TemplateName::Template:
- TD = TN.getAsTemplateDecl();
- goto HaveDecl;
+ llvm_unreachable("unexpected UsingTemplate outside of a QualifiedTemplate");
- HaveDecl:
+ case TemplateName::Template: {
+ TemplateDecl *TD = TN.getAsTemplateDecl();
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TD))
mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
else
mangleName(TD);
break;
+ }
case TemplateName::OverloadedTemplate:
case TemplateName::AssumedTemplate:
@@ -3003,53 +3093,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 =
@@ -3083,10 +3133,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)); \
@@ -3794,8 +3840,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>()) {
@@ -3823,10 +3869,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>
@@ -3838,7 +3882,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>
@@ -4475,18 +4519,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) {
@@ -4653,6 +4694,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();
@@ -7043,8 +7123,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());
@@ -7213,8 +7294,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/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..1f20e2aee0619 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -994,6 +994,8 @@ 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);
}
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index a7e7006c98a1b..371a6bdbbefa6 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() {}
@@ -49,6 +52,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"
@@ -84,13 +89,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 {
@@ -100,7 +104,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());
@@ -114,7 +118,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;
}
@@ -156,8 +160,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());
}
@@ -171,14 +175,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);
}
@@ -188,7 +194,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:
@@ -1457,8 +1464,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);
@@ -1586,6 +1593,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) {
@@ -1632,6 +1640,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) {
@@ -2233,6 +2242,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) {
@@ -2273,6 +2283,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 {
@@ -2986,10 +2997,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..0ddf49bb19b5c 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;
}
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 19b85d0e0af69..d501681f0342c 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -463,9 +463,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();
@@ -4160,13 +4167,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;
@@ -4183,13 +4187,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;
@@ -4199,7 +4200,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;
@@ -4271,19 +4274,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())
@@ -4292,12 +4293,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,
@@ -4710,29 +4712,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;
@@ -4767,23 +4765,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/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index ebbc0addfed2c..fd70eb965f636 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -105,8 +105,9 @@ RValue CodeGenFunction::EmitCXXDestructorCall(
const CXXMethodDecl *DtorDecl = cast<CXXMethodDecl>(Dtor.getDecl());
assert(!ThisTy.isNull());
- assert(ThisTy->getAsCXXRecordDecl() == DtorDecl->getParent() &&
- "Pointer/Object mixup");
+ assert(
+ declaresSameEntity(ThisTy->getAsCXXRecordDecl(), DtorDecl->getParent()) &&
+ "Pointer/Object mixup");
LangAS SrcAS = ThisTy.getAddressSpace();
LangAS DstAS = DtorDecl->getMethodQualifiers().getAddressSpace();
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 5006af55975ca..3b01201510351 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -311,12 +311,6 @@ addVectorTexturePartialSpecialization(Sema &S, NamespaceDecl *HLSLNamespace,
AST.IntTy, VK_LValue),
SourceLocation());
- // Create the partial specialization declaration.
- QualType CanonInjectedTST =
- AST.getCanonicalType(AST.getTemplateSpecializationType(
- ElaboratedTypeKeyword::Class, TemplateName(TextureTemplate),
- {TemplateArgument(VectorType)}, {}));
-
// Set the template arguments as written.
TemplateArgument Arg(VectorType);
TemplateArgumentLoc ArgLoc =
@@ -325,12 +319,13 @@ addVectorTexturePartialSpecialization(Sema &S, NamespaceDecl *HLSLNamespace,
TemplateArgumentListInfo(SourceLocation(), SourceLocation());
ArgsInfo.addArgument(ArgLoc);
+ // Create the partial specialization declaration.
auto *PartialSpec = ClassTemplatePartialSpecializationDecl::Create(
AST, TagDecl::TagKind::Class, HLSLNamespace, SourceLocation(),
SourceLocation(), TemplateParams,
ASTTemplateArgumentListInfo::Create(AST, ArgsInfo), TextureTemplate,
{TemplateArgument(VectorType)},
- CanQualType::CreateUnsafe(CanonInjectedTST), nullptr);
+ /*PrevDecl=*/nullptr);
PartialSpec->setImplicit(true);
PartialSpec->setLexicalDeclContext(HLSLNamespace);
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 255c22d9c2a31..cd0040b3c9fa6 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -48,112 +48,114 @@ DeclContext *Sema::computeDeclContext(QualType T) {
DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
bool EnteringContext) {
- if (!SS.isSet() || SS.isInvalid())
- return nullptr;
-
NestedNameSpecifier NNS = SS.getScopeRep();
- if (NNS.isDependent()) {
- // If this nested-name-specifier refers to the current
- // instantiation, return its DeclContext.
- if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
- return Record;
-
- if (EnteringContext) {
- if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
- return nullptr;
- const Type *NNSType = NNS.getAsType();
-
- // Look through type alias templates, per C++0x [temp.dep.type]p1.
- NNSType = Context.getCanonicalType(NNSType);
- if (const auto *SpecType =
- dyn_cast<TemplateSpecializationType>(NNSType)) {
- // 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())) {
- // 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
- // correctly. Currently removing this fallback would change the
- // diagnostic output for invalid code in a number of tests.
- ClassTemplatePartialSpecializationDecl *PartialSpec = nullptr;
- ArrayRef<TemplateParameterList *> TemplateParamLists =
- SS.getTemplateParamLists();
- if (!TemplateParamLists.empty()) {
- unsigned Depth = ClassTemplate->getTemplateParameters()->getDepth();
- auto L = find_if(TemplateParamLists,
- [Depth](TemplateParameterList *TPL) {
- return TPL->getDepth() == Depth;
- });
- if (L != TemplateParamLists.end()) {
- void *Pos = nullptr;
- PartialSpec = ClassTemplate->findPartialSpecialization(
- SpecType->template_arguments(), *L, Pos);
- }
- } else {
- PartialSpec =
- ClassTemplate->findPartialSpecialization(QualType(SpecType, 0));
- }
-
- if (PartialSpec) {
- // A declaration of the partial specialization must be visible.
- // We can always recover here, because this only happens when we're
- // entering the context, and that can't happen in a SFINAE context.
- assert(!isSFINAEContext() && "partial specialization scope "
- "specifier in SFINAE context?");
- if (PartialSpec->hasDefinition() &&
- !hasReachableDefinition(PartialSpec))
- diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec,
- MissingImportKind::PartialSpecialization,
- true);
- return PartialSpec;
- }
-
- // If the type of the nested name specifier is the same as the
- // injected class name of the named class template, we're entering
- // into that class template definition.
- CanQualType Injected =
- ClassTemplate->getCanonicalInjectedSpecializationType(Context);
- if (Context.hasSameType(Injected, QualType(SpecType, 0)))
- return ClassTemplate->getTemplatedDecl();
- }
- } else if (const auto *RecordT = dyn_cast<RecordType>(NNSType)) {
- // The nested name specifier refers to a member of a class template.
- return RecordT->getDecl()->getDefinitionOrSelf();
- }
+ if (!NNS.isDependent()) {
+ switch (NNS.getKind()) {
+ case NestedNameSpecifier::Kind::Namespace:
+ return const_cast<NamespaceDecl *>(
+ NNS.getAsNamespaceAndPrefix().Namespace->getNamespace());
+ case NestedNameSpecifier::Kind::Type:
+ return NNS.getAsType()->getAs<TagType>()->getDecl();
+ case NestedNameSpecifier::Kind::Global:
+ return Context.getTranslationUnitDecl();
+ case NestedNameSpecifier::Kind::MicrosoftSuper:
+ return NNS.getAsMicrosoftSuper();
+ case NestedNameSpecifier::Kind::Null:
+ return nullptr;
}
-
- return nullptr;
+ llvm_unreachable("Invalid NestedNameSpecifier::Kind!");
}
+ // If this nested-name-specifier refers to the current
+ // instantiation, return its DeclContext.
+ if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
+ return Record;
- switch (NNS.getKind()) {
- case NestedNameSpecifier::Kind::Namespace:
- return const_cast<NamespaceDecl *>(
- NNS.getAsNamespaceAndPrefix().Namespace->getNamespace());
-
- case NestedNameSpecifier::Kind::Type:
- return NNS.getAsType()->castAsTagDecl();
+ if (!EnteringContext || NNS.getKind() != NestedNameSpecifier::Kind::Type)
+ return nullptr;
- case NestedNameSpecifier::Kind::Global:
- return Context.getTranslationUnitDecl();
+ const Type *NNSType = NNS.getAsType();
+ // As an extension, look through type alias templates.
+ // Shouldn't do this per P1787R6 and CWG2858.
+ if (const auto *SpecType =
+ 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.
+ ClassTemplateDecl *ClassTemplate = dyn_cast_or_null<ClassTemplateDecl>(
+ SpecType->getTemplateName().getAsTemplateDecl(
+ /*IgnoreDeduced=*/true));
+ if (!ClassTemplate)
+ return nullptr;
+ const auto *CanonSpecType =
+ cast<TemplateSpecializationType>(Context.getCanonicalType(
+ QualType(SpecType, 0), CanonicalizationKind::Functional));
+ ClassTemplatePartialSpecializationDecl *PartialSpec = nullptr;
+ ArrayRef<TemplateParameterList *> TemplateParamLists =
+ SS.getTemplateParamLists();
+ if (!TemplateParamLists.empty()) {
+ unsigned Depth = ClassTemplate->getTemplateParameters()->getDepth();
+ auto L = find_if(TemplateParamLists, [Depth](TemplateParameterList *TPL) {
+ return TPL->getDepth() == Depth;
+ });
+ if (L != TemplateParamLists.end()) {
+ // 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");
+ Context.canonicalizeTemplateArguments(CTAI.SugaredConverted,
+ CanonicalizationKind::Functional);
+
+ void *Pos = nullptr;
+ PartialSpec = ClassTemplate->findPartialSpecialization(
+ CTAI.SugaredConverted, *L, Pos);
+ }
+ } else {
+ // FIXME: This fallback on the search of partial specialization using the
+ // specialization type should be eventually removed since it doesn't
+ // handle the case of constrained template parameters correctly.
+ // Currently removing this fallback would change the diagnostic output for
+ // invalid code in a number of tests.
+ PartialSpec =
+ ClassTemplate->findPartialSpecialization(QualType(CanonSpecType, 0));
+ }
- case NestedNameSpecifier::Kind::MicrosoftSuper:
- return NNS.getAsMicrosoftSuper();
+ if (PartialSpec) {
+ // A declaration of the partial specialization must be visible.
+ // We can always recover here, because this only happens when we're
+ // entering the context, and that can't happen in a SFINAE context.
+ assert(!isSFINAEContext() && "partial specialization scope "
+ "specifier in SFINAE context?");
+ if (PartialSpec->hasDefinition() && !hasReachableDefinition(PartialSpec))
+ diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec,
+ MissingImportKind::PartialSpecialization, true);
+ return PartialSpec;
+ }
- case NestedNameSpecifier::Kind::Null:
- llvm_unreachable("unexpected null nested name specifier");
+ // If the type of the nested name specifier is the same as the
+ // injected class name of the named class template, we're entering
+ // into that class template definition.
+ CanQualType Injected =
+ ClassTemplate->getCanonicalInjectedSpecializationType(Context);
+ if (Injected->getTypePtr() != CanonSpecType)
+ return nullptr;
+ return ClassTemplate->getTemplatedDecl();
}
-
- llvm_unreachable("Invalid NestedNameSpecifier::Kind!");
-}
-
-bool Sema::isDependentScopeSpecifier(const CXXScopeSpec &SS) {
- if (!SS.isSet() || SS.isInvalid())
- return false;
-
- return SS.getScopeRep().isDependent();
+ if (auto *TT = NNSType->getAs<TagType>()) {
+ // The nested name specifier refers to a member of a class template.
+ return dyn_cast<CXXRecordDecl>(TT->getDecl());
+ }
+ return nullptr;
}
CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier NNS) {
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 32a07b6266666..0d2b5c44f6db9 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1598,10 +1598,8 @@ bool Sema::AreConstraintExpressionsEqual(const Decl *Old, const Expr *OldConstr,
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(
@@ -2563,11 +2561,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)
+ // 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 54b20c2088c34..e29e6ee39e1ef 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2569,10 +2569,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.hasEquivalentType(OldType, NewType)) {
int Kind = isa<TypeAliasDecl>(Old) ? 1 : 0;
Diag(New->getLocation(), diag::err_redefinition_different_typedef)
<< Kind << NewType << OldType;
@@ -4575,7 +4574,7 @@ 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.hasEquivalentType(New->getType(), Old->getType()));
S.Diag(New->getLocation(), New->isThisDeclarationADefinition()
? diag::err_redefinition_different_type
@@ -4600,7 +4599,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.hasEquivalentType(New->getType(), Old->getType())) {
// These could still be something that needs exception specs checked.
return MergeVarDeclExceptionSpecs(New, Old);
}
@@ -4609,7 +4609,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());
@@ -4623,26 +4623,28 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
if (PrevVDTy->isIncompleteArrayType() || PrevVDTy->isDependentType())
continue;
- if (!Context.hasSameType(New->getType(), PrevVDTy))
+ if (!Context.hasEquivalentType(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.hasEquivalentType(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 45da1eec7d517..e539ed25a9a9f 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -13952,8 +13952,8 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,
TypeAliasDecl *OldTD = OldDecl->getTemplatedDecl();
if (!Invalid &&
- !Context.hasSameType(OldTD->getUnderlyingType(),
- NewTD->getUnderlyingType())) {
+ !Context.hasEquivalentType(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/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index dc7f874927a8e..b6099b37104f8 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -465,9 +465,10 @@ ParsedType Sema::getDestructorTypeForDecltype(const DeclSpec &DS,
return nullptr;
}
+ Expr *E = DS.getRepAsExpr();
assert(DS.getTypeSpecType() == DeclSpec::TST_decltype &&
"unexpected type in getDestructorType");
- QualType T = BuildDecltypeType(DS.getRepAsExpr());
+ QualType T = BuildDecltypeType(E);
// If we know the type of the object, check that the correct destructor
// type was named now; we can give better diagnostics this way.
@@ -483,6 +484,7 @@ ParsedType Sema::getDestructorTypeForDecltype(const DeclSpec &DS,
DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
+ DecltypeTL.setUnderlyingExpr(E);
return CreateParsedType(T, TLB.getTypeSourceInfo(Context, T));
}
@@ -8090,15 +8092,17 @@ Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc,
PushDeclContext(BodyScope, Body);
for (ParmVarDecl *Param : LocalParameters) {
- if (Param->getType()->isVoidType()) {
+ if (QualType PT = Param->getType(); PT->isVoidType()) {
if (LocalParameters.size() > 1) {
Diag(Param->getBeginLoc(), diag::err_void_only_param);
Param->setType(Context.IntTy);
} else if (Param->getIdentifier()) {
Diag(Param->getBeginLoc(), diag::err_param_with_void_type);
Param->setType(Context.IntTy);
- } else if (Param->getType().hasQualifiers()) {
+ } else if (PT.hasQualifiers()) {
Diag(Param->getBeginLoc(), diag::err_void_param_qualified);
+ } else if (!Context.hasEquivalentType(PT, Context.VoidTy)) {
+ Diag(Param->getBeginLoc(), diag::err_void_param_not_equivalent_to_void);
}
} else if (Param->hasDefaultArg()) {
// C++2a [expr.prim.req] p4
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 0b819d05e72ef..049510c416822 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1901,8 +1901,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();
@@ -1913,7 +1911,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/SemaOpenACCAtomic.cpp b/clang/lib/Sema/SemaOpenACCAtomic.cpp
index ad21129d30c15..dbe7e9739c2f9 100644
--- a/clang/lib/Sema/SemaOpenACCAtomic.cpp
+++ b/clang/lib/Sema/SemaOpenACCAtomic.cpp
@@ -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 0f45a21c5e461..61da3398137d0 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3541,12 +3541,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;
@@ -21258,13 +21254,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 55ffdced0ea13..60992b6a110ad 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1372,8 +1372,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
@@ -1420,7 +1422,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.hasEquivalentType(
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
@@ -1619,10 +1621,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;
}
@@ -3532,7 +3531,7 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
}
// No extra info for same types.
- if (Context.hasSameType(FromType, ToType)) {
+ if (Context.hasEquivalentType(FromType, ToType)) {
PDiag << ft_default;
return;
}
@@ -3562,8 +3561,8 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
}
// Handle different return type.
- if (!Context.hasSameType(FromFunction->getReturnType(),
- ToFunction->getReturnType())) {
+ if (!Context.hasEquivalentType(FromFunction->getReturnType(),
+ ToFunction->getReturnType())) {
PDiag << ft_return_type << ToFunction->getReturnType()
<< FromFunction->getReturnType();
return;
@@ -3607,7 +3606,10 @@ bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
QualType NewType =
Context.removePtrSizeAddrSpace((New.begin() + J)->getUnqualifiedType());
- if (!Context.hasSameType(OldType, NewType)) {
+ assert(Context.isFunctionalCanonicalType(OldType));
+ assert(Context.isFunctionalCanonicalType(NewType));
+
+ if (OldType != NewType) {
if (ArgPos)
*ArgPos = Idx;
return false;
@@ -3619,6 +3621,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);
}
@@ -3637,8 +3643,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),
@@ -10781,8 +10794,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 1591cea9286ae..0365183ee1b09 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3570,7 +3570,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);
}
@@ -4181,8 +4187,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;
}
@@ -4205,9 +4210,22 @@ static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
llvm_unreachable("unexpected kind of template argument");
}
-static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
+// This function implements the following rule:
+//
+// C++ 14 [temp.class.spec]p8b3:
+//
+// -- The argument list of the specialization shall not be identical
+// to the implicit argument list of the primary template.
+//
+// This rule has since been removed, because it's redundant given CWG1495,
+// but we keep it because it produces better diagnostics and recovery.
+// If the template parameter list is constrained, we ignore this.
+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;
@@ -4234,8 +4252,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.hasEquivalentType(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, TTP->getTemplateParameters(), SpecTTP,
+ SpecTTP->getTemplateParameters(),
+ /*Complain=*/false, Sema::TPL_TemplateParamsEquivalent))
return false;
}
}
@@ -4416,32 +4446,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())) {
- // C++ [temp.class.spec]p9b3:
- //
- // -- The argument list of the specialization shall not be identical
- // to the implicit argument list of the primary template.
+ FunctionallyEquivalentConverted = CTAI.SugaredConverted;
+ Context.canonicalizeTemplateArguments(FunctionallyEquivalentConverted,
+ CanonicalizationKind::Functional);
+
+ if (isSameAsPrimaryTemplate(*this, VarTemplate->getTemplateParameters(),
+ TemplateParams,
+ FunctionallyEquivalentConverted)) {
Diag(TemplateNameLoc, diag::err_partial_spec_args_match_primary_template)
<< /*variable template*/ 1
<< /*is definition*/ (SC != SC_Extern && !CurContext->isRecord())
@@ -4457,7 +4489,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
if (IsPartialSpecialization)
PrevDecl = VarTemplate->findPartialSpecialization(
- CTAI.CanonicalConverted, TemplateParams, InsertPos);
+ FunctionallyEquivalentConverted, TemplateParams, InsertPos);
else
PrevDecl =
VarTemplate->findSpecialization(CTAI.CanonicalConverted, InsertPos);
@@ -4488,7 +4520,8 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
Context, VarTemplate->getDeclContext(), TemplateKWLoc,
TemplateNameLoc, TemplateParams,
ASTTemplateArgumentListInfo::Create(Context, TemplateArgs),
- VarTemplate, TSI->getType(), TSI, SC, CTAI.CanonicalConverted);
+ VarTemplate, TSI->getType(), TSI, SC,
+ FunctionallyEquivalentConverted);
if (!PrevPartial)
VarTemplate->AddPartialSpecialization(Partial, InsertPos);
@@ -4898,9 +4931,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(
@@ -8271,7 +8304,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.hasEquivalentType(OldType, NewType)) {
if (Complain) {
unsigned NextDiag = diag::err_template_nontype_parm_different_type;
if (TemplateArgLoc.isValid()) {
@@ -8916,14 +8949,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;
@@ -8931,15 +8964,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;
@@ -8965,23 +9006,9 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
TemplateParams,
ASTTemplateArgumentListInfo::Create(Context, TemplateArgs));
} 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())) {
- // C++ [temp.class.spec]p9b3:
- //
- // -- The argument list of the specialization shall not be identical
- // to the implicit argument list of the primary template.
- //
- // This rule has since been removed, because it's redundant given DR1495,
- // but we keep it because it produces better diagnostics and recovery.
+ if (isSameAsPrimaryTemplate(*this, ClassTemplate->getTemplateParameters(),
+ TemplateParams,
+ FunctionallyEquivalentConverted)) {
Diag(TemplateNameLoc, diag::err_partial_spec_args_match_primary_template)
<< /*class template*/ 0 << (TUK == TagUseKind::Definition)
<< FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc));
@@ -9000,7 +9027,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
ClassTemplatePartialSpecializationDecl::Create(
Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
ASTTemplateArgumentListInfo::Create(Context, TemplateArgs),
- ClassTemplate, CTAI.CanonicalConverted, CanonType, PrevPartial);
+ ClassTemplate, FunctionallyEquivalentConverted, PrevPartial);
if (!PrevPartial)
ClassTemplate->AddPartialSpecialization(Partial, InsertPos);
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f57f60eb527ae..1b353d0e0092d 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.
@@ -6265,11 +6261,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;
}
@@ -6284,13 +6283,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;
}
@@ -6438,11 +6438,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);
@@ -6457,10 +6463,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 aad0d7da9420a..2ee5194d3cbe9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -50,7 +50,7 @@ static bool isDeclWithinFunction(const Decl *D) {
}
template <typename DeclT>
-static bool SubstQualifier(Sema &SemaRef, const DeclT *OldDecl, DeclT *NewDecl,
+static void SubstQualifier(Sema &SemaRef, const DeclT *OldDecl, DeclT *NewDecl,
const MultiLevelTemplateArgumentList &TemplateArgs,
DeclContext *Owner) {
if (auto TPLs = OldDecl->getTemplateParameterLists(); !TPLs.empty()) {
@@ -89,23 +89,20 @@ static bool SubstQualifier(Sema &SemaRef, const DeclT *OldDecl, DeclT *NewDecl,
NestedNameSpecifierLoc NewQualifierLoc =
SemaRef.SubstNestedNameSpecifierLoc(OldDecl->getQualifierLoc(),
TemplateArgs);
-
- if (!NewQualifierLoc)
- return true;
-
+ assert(NewQualifierLoc &&
+ "substitution for the declaration qualifiers should never fail");
NewDecl->setQualifierInfo(NewQualifierLoc);
}
- return false;
}
-bool TemplateDeclInstantiator::SubstQualifier(const DeclaratorDecl *OldDecl,
+void TemplateDeclInstantiator::SubstQualifier(const DeclaratorDecl *OldDecl,
DeclaratorDecl *NewDecl) {
- return ::SubstQualifier(SemaRef, OldDecl, NewDecl, TemplateArgs, Owner);
+ ::SubstQualifier(SemaRef, OldDecl, NewDecl, TemplateArgs, Owner);
}
-bool TemplateDeclInstantiator::SubstQualifier(const TagDecl *OldDecl,
+void TemplateDeclInstantiator::SubstQualifier(const TagDecl *OldDecl,
TagDecl *NewDecl) {
- return ::SubstQualifier(SemaRef, OldDecl, NewDecl, TemplateArgs, Owner);
+ ::SubstQualifier(SemaRef, OldDecl, NewDecl, TemplateArgs, Owner);
}
// Include attribute instantiation code.
@@ -1806,8 +1803,7 @@ TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
SemaRef.deduceOpenCLAddressSpace(Var);
// Substitute the nested name specifier, if any.
- if (SubstQualifier(D, Var))
- return nullptr;
+ SubstQualifier(D, Var);
SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner,
StartingScope, InstantiatingVarTemplate);
@@ -2195,7 +2191,7 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
// If it did, mark the new tag as being associated with that typedef.
if (TypedefNameDecl *TND = SemaRef.Context.getTypedefNameForUnnamedTagDecl(D))
SemaRef.Context.addTypedefNameForUnnamedTagDecl(Enum, TND);
- if (SubstQualifier(D, Enum)) return nullptr;
+ SubstQualifier(D, Enum);
Owner->addDecl(Enum);
EnumDecl *Def = D->getDefinition();
@@ -2641,8 +2637,7 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
Record->setImplicit(D->isImplicit());
// Substitute the nested name specifier, if any.
- if (SubstQualifier(D, Record))
- return nullptr;
+ SubstQualifier(D, Record);
SemaRef.InstantiateAttrsForDecl(TemplateArgs, D, Record, LateAttrs,
StartingScope);
@@ -3371,8 +3366,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
}
- if (SubstQualifier(D, Method))
- return nullptr;
+ SubstQualifier(D, Method);
// If we are instantiating a member function defined
// out-of-line, the instantiation will have the same lexical
@@ -4653,8 +4647,7 @@ TemplateDeclInstantiator::VisitClassTemplateSpecializationDecl(
InstClassTemplate->AddSpecialization(InstD, InsertPos);
// Substitute the nested name specifier, if any.
- if (SubstQualifier(D, InstD))
- return nullptr;
+ SubstQualifier(D, InstD);
InstD->setAccess(D->getAccess());
InstD->setInstantiationOfMemberClass(D, TSK_ImplicitInstantiation);
@@ -4775,8 +4768,7 @@ TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl(
SemaRef.deduceOpenCLAddressSpace(Var);
// Substitute the nested name specifier, if any.
- if (SubstQualifier(D, Var))
- return nullptr;
+ SubstQualifier(D, Var);
SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner,
StartingScope, false, PrevDecl);
@@ -4992,14 +4984,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.
@@ -5010,13 +5007,11 @@ TemplateDeclInstantiator::InstantiateClassTemplatePartialSpecialization(
PartialSpec->getBeginLoc(), PartialSpec->getLocation(), InstParams,
ASTTemplateArgumentListInfo::Create(SemaRef.Context,
InstTemplateArgs),
- ClassTemplate, CTAI.CanonicalConverted,
- /*CanonInjectedTST=*/CanQualType(),
+ ClassTemplate, FunctionallyEquivalentConverted,
/*PrevDecl=*/nullptr);
// Substitute the nested name specifier, if any.
- if (SubstQualifier(PartialSpec, InstPartialSpec))
- return nullptr;
+ SubstQualifier(PartialSpec, InstPartialSpec);
InstPartialSpec->setInstantiatedFromMember(PartialSpec);
@@ -5102,14 +5097,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
@@ -5134,11 +5134,10 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
ASTTemplateArgumentListInfo::Create(SemaRef.Context,
InstTemplateArgs),
VarTemplate, TSI->getType(), TSI, PartialSpec->getStorageClass(),
- CTAI.CanonicalConverted);
+ FunctionallyEquivalentConverted);
// Substitute the nested name specifier, if any.
- if (SubstQualifier(PartialSpec, InstPartialSpec))
- return nullptr;
+ SubstQualifier(PartialSpec, InstPartialSpec);
InstPartialSpec->setInstantiatedFromMember(PartialSpec);
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index de5f52f53ec29..923459eb2615e 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1412,13 +1412,12 @@ TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern(
}
case TemplateArgument::Expression: {
- PackExpansionExpr *Expansion
- = cast<PackExpansionExpr>(Argument.getAsExpr());
- Expr *Pattern = Expansion->getPattern();
+ auto *Expansion = cast<PackExpansionExpr>(OrigLoc.getSourceExpression());
Ellipsis = Expansion->getEllipsisLoc();
NumExpansions = Expansion->getNumExpansions();
+ Expr *Pattern = Expansion->getPattern();
return TemplateArgumentLoc(
- TemplateArgument(Pattern, Argument.isCanonicalExpr()), Pattern);
+ TemplateArgument(Pattern, /*CanonKind=*/std::nullopt), Pattern);
}
case TemplateArgument::TemplateExpansion:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 1378c7baca92e..d27130438a500 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5287,6 +5287,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// Reject, but continue to parse 'float(const void)'.
if (ParamTy.hasQualifiers())
S.Diag(DeclType.Loc, diag::err_void_param_qualified);
+ else if (!Context.hasEquivalentType(ParamTy, Context.VoidTy))
+ S.Diag(DeclType.Loc,
+ diag::err_void_param_not_equivalent_to_void);
for (const auto *A : Param->attrs()) {
S.Diag(A->getLoc(), diag::warn_attribute_on_void_param)
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 3c8fcbe582b43..49e643e04c426 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7075,15 +7075,14 @@ 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();
DecltypeTypeLoc NewTL = TLB.push<DecltypeTypeLoc>(Result);
+ NewTL.setUnderlyingExpr(E.get());
NewTL.setDecltypeLoc(TL.getDecltypeLoc());
NewTL.setRParenLoc(TL.getRParenLoc());
return Result;
diff --git a/clang/test/AST/ast-dump-templates-pattern.cpp b/clang/test/AST/ast-dump-templates-pattern.cpp
index 705c9c967c7ef..90880c5bdd592 100644
--- a/clang/test/AST/ast-dump-templates-pattern.cpp
+++ b/clang/test/AST/ast-dump-templates-pattern.cpp
@@ -1,11 +1,11 @@
// RUN: %clang_cc1 -triple x86_64 -std=c++26 -ast-dump -ast-dump-filter=Test %s \
-// RUN: | FileCheck --match-full-lines %s
+// RUN: | FileCheck --match-full-lines --check-prefixes CHECK,CHECK-SRC %s
// Test with serialization:
// RUN: %clang_cc1 -triple x86_64 -std=c++26 -emit-pch -o %t %s
// RUN: %clang_cc1 -triple x86_64 -x c++ -std=c++26 -include-pch %t -ast-dump-all -ast-dump-filter=Test /dev/null \
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
-// RUN: | FileCheck --match-full-lines %s
+// RUN: | FileCheck --match-full-lines --check-prefixes CHECK,CHECK-PCH %s
namespace TestClassRedecl {
template <class T> struct A {};
@@ -71,22 +71,25 @@ namespace TestNestedClassRedecl {
template <class T> struct A;
template <class T> template <class U> struct A<T>::B {};
template struct A<int>::B<char>;
+// FIXME: Serialization doesn't preserve exact parent because DeclContext merging doesn't discriminate redeclarations properly.
// CHECK-LABEL: Dumping TestNestedClassRedecl:
-// CHECK: |-ClassTemplateDecl 0x[[TestNestedClassRedecl_A_T1:[^ ]+]] <line:[[@LINE-7]]:{{.+}} A external-linkage
-// CHECK: | |-CXXRecordDecl 0x[[TestNestedClassRedecl_A_D1:[^ ]+]] <{{.+}}line:[[@LINE-6]]:{{.+}}> line:[[@LINE-8]]:{{.+}} struct A definition
-// CHECK: | | `-ClassTemplateDecl 0x[[TestNestedClassRedecl_B_T1:[^ ]+]] <line:[[@LINE-8]]:{{.+}} B external-linkage
+// CHECK: |-ClassTemplateDecl 0x[[TestNestedClassRedecl_A_T1:[^ ]+]] <line:[[@LINE-8]]:{{.+}} A external-linkage
+// CHECK: | |-CXXRecordDecl 0x[[TestNestedClassRedecl_A_D1:[^ ]+]] <{{.+}}line:[[@LINE-7]]:{{.+}}> line:[[@LINE-9]]:{{.+}} struct A definition
+// CHECK: | | `-ClassTemplateDecl 0x[[TestNestedClassRedecl_B_T1:[^ ]+]] <line:[[@LINE-9]]:{{.+}} B external-linkage
// CHECK: | | `-CXXRecordDecl 0x[[TestNestedClassRedecl_B_D1:[^ ]+]] {{.+}} struct B
-// CHECK: | `-ClassTemplateSpecializationDecl 0x[[TestNestedClassRedecl_A_S1:[^ ]+]] <line:[[@LINE-8]]:{{.+}} line:[[@LINE-11]]:{{.+}} struct A definition external-linkage instantiated_from 0x[[TestNestedClassRedecl_A_D2:[^ ]+]] implicit_instantiation
-// CHECK: | |-ClassTemplateDecl 0x{{.+}} <line:[[@LINE-11]]:{{.+}} B external-linkage
+// CHECK: | `-ClassTemplateSpecializationDecl 0x[[TestNestedClassRedecl_A_S1:[^ ]+]] <line:[[@LINE-9]]:{{.+}} line:[[@LINE-12]]:{{.+}} struct A definition external-linkage instantiated_from 0x[[TestNestedClassRedecl_A_D2:[^ ]+]] implicit_instantiation
+// CHECK: | |-ClassTemplateDecl 0x{{.+}} <line:[[@LINE-12]]:{{.+}} B external-linkage
// CHECK: | | |-CXXRecordDecl 0x{{.+}} struct B
// CHECK: | | `-ClassTemplateSpecialization 0x[[TestNestedClassRedecl_B_S1:[^ ]+]] 'B'
-// CHECK: | `-ClassTemplateSpecializationDecl 0x[[TestNestedClassRedecl_B_S1]] <line:[[@LINE-10]]:{{.+}} struct B definition external-linkage instantiated_from 0x[[TestNestedClassRedecl_B_D1]] explicit_instantiation_definition
-// CHECK: |-ClassTemplateDecl 0x{{.+}} prev 0x[[TestNestedClassRedecl_A_T1]] <line:[[@LINE-13]]:{{.+}} A external-linkage
+// CHECK: | `-ClassTemplateSpecializationDecl 0x[[TestNestedClassRedecl_B_S1]] <line:[[@LINE-11]]:{{.+}} struct B definition external-linkage instantiated_from 0x[[TestNestedClassRedecl_B_D1]] explicit_instantiation_definition
+// CHECK: |-ClassTemplateDecl 0x{{.+}} prev 0x[[TestNestedClassRedecl_A_T1]] <line:[[@LINE-14]]:{{.+}} A external-linkage
// CHECK: | |-CXXRecordDecl 0x[[TestNestedClassRedecl_A_D2]] prev 0x[[TestNestedClassRedecl_A_D1]] {{.+}} struct A
// CHECK: | `-ClassTemplateSpecialization 0x[[TestNestedClassRedecl_A_S1]] 'A'
-// CHECK: |-ClassTemplateDecl 0x{{.+}} parent 0x[[TestNestedClassRedecl_A_D1]] prev 0x[[TestNestedClassRedecl_B_T1]] <line:[[@LINE-15]]:{{.+}} B external-linkage
-// CHECK: | `-CXXRecordDecl 0x[[TestNestedClassRedecl_B_D2:[^ ]+]] parent 0x[[TestNestedClassRedecl_A_D1]] prev 0x[[TestNestedClassRedecl_B_D1]] {{.+}} struct B definition
-// CHECK: `-ExplicitInstantiationDecl {{.+}} <line:[[@LINE-16]]:{{.+}} 'B'
+// CHECK-SRC: |-ClassTemplateDecl 0x{{.+}} parent 0x[[TestNestedClassRedecl_A_D2]] prev 0x[[TestNestedClassRedecl_B_T1]] <line:[[@LINE-16]]:{{.+}} B external-linkage
+// CHECK-SRC: | `-CXXRecordDecl 0x[[TestNestedClassRedecl_B_D2:[^ ]+]] parent 0x[[TestNestedClassRedecl_A_D2]] prev 0x[[TestNestedClassRedecl_B_D1]] {{.+}} struct B definition
+// CHECK-PCH: |-ClassTemplateDecl 0x{{.+}} parent 0x[[TestNestedClassRedecl_A_D1]] prev 0x[[TestNestedClassRedecl_B_T1]] <line:[[@LINE-18]]:{{.+}} B external-linkage
+// CHECK-PCH: | `-CXXRecordDecl 0x[[TestNestedClassRedecl_B_D2:[^ ]+]] parent 0x[[TestNestedClassRedecl_A_D1]] prev 0x[[TestNestedClassRedecl_B_D1]] {{.+}} struct B definition
+// CHECK: `-ExplicitInstantiationDecl {{.+}} <line:[[@LINE-19]]:{{.+}} 'B'
// CHECK: |-NestedNameSpecifier TypeSpec {{.+}}
// CHECK: |-ClassTemplateSpecialization 0x[[TestNestedClassRedecl_B_S1]] 'B'
// CHECK: `-TemplateArgument {{.+}} type 'char'
diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp
index 22b4e8fb1dec7..fb202b33c87f1 100644
--- a/clang/test/AST/ast-dump-templates.cpp
+++ b/clang/test/AST/ast-dump-templates.cpp
@@ -502,7 +502,7 @@ namespace TestPartialSpecNTTP {
// 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": {{.+}},
@@ -519,7 +519,7 @@ namespace TestPartialSpecNTTP {
// JSON-NEXT: "qualType": "bool"
// JSON: "kind": "TemplateArgument",
// JSON-NEXT: "isExpr": true,
-// JSON-NEXT: "isCanonical": true,
+// JSON-NEXT: "CanonicalKind": "Structural",
// JSON-NEXT: "inner": [
// JSON-NEXT: {
// JSON-NEXT: "id": {{.+}},
@@ -642,7 +642,7 @@ namespace TestPartialSpecNTTP {
// 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": {{.+}},
@@ -659,7 +659,7 @@ namespace TestPartialSpecNTTP {
// JSON-NEXT: "qualType": "bool"
// JSON: "kind": "TemplateArgument",
// JSON-NEXT: "isExpr": true,
-// JSON-NEXT: "isCanonical": true,
+// JSON-NEXT: "CanonicalKind": "Structural",
// JSON-NEXT: "inner": [
// JSON-NEXT: {
// JSON-NEXT: "id": {{.+}},
diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 8162f9caa8f15..5cc48b9e874aa 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: 23 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/drs/cwg31xx.cpp b/clang/test/CXX/drs/cwg31xx.cpp
index 512f3cfa2ca92..70286f2c64500 100644
--- a/clang/test/CXX/drs/cwg31xx.cpp
+++ b/clang/test/CXX/drs/cwg31xx.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,cxx98-14
+// RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,cxx98,cxx98-14
// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,cxx98-14,cxx11-17,since-cxx11
// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,cxx98-14,cxx14-17,cxx11-17,since-cxx11,since-cxx14
// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,since-cxx17
@@ -6,7 +6,7 @@
// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,since-cxx11,since-cxx14,since-cxx17,since-cxx20
// RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,since-cxx11,since-cxx14,since-cxx17,since-cxx20
-// RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,cxx98-14
+// RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,cxx98,cxx98-14
// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,cxx98-14,cxx11-17,since-cxx11
// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,cxx98-14,cxx14-17,cxx11-17,since-cxx11,since-cxx14
// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions -pedantic-errors -fexperimental-new-constant-interpreter -verify-directives -verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,since-cxx17
@@ -55,7 +55,7 @@ struct Pinned {
struct Source {
operator Pinned&&() const;
-
+
template<int>
Source get() noexcept;
};
@@ -69,13 +69,13 @@ namespace std {
struct tuple_size<cwg3135::Source> {
static constexpr int value = 1;
};
-
+
template<>
struct tuple_element<0, cwg3135::Source> { using type = cwg3135::Pinned; };
} // namespace std
namespace cwg3135 {
-// CWG3135: `x` is of type Pinned rather than Pinned&&.
+// CWG3135: `x` is of type Pinned rather than Pinned&&.
// This leads to the deleted copy ctor being called.
auto [x] = Source{};
// since-cxx17-error at -1 {{initializing binding of type 'Pinned' invokes deleted constructor}}
@@ -108,3 +108,22 @@ decltype([b = C(3)](){ return 4; }()) x;
} // namespace cwg3156
// cwg3172: na
+
+namespace cwg3179 { // cwg3179: 23 tentatively ready 2026-04-30
+#if __cplusplus >= 201103L
+ template<class> using void_t = void;
+ template<class T> struct S {
+ void f(void_t<T*>);
+ // expected-error at -1 {{'void' as parameter must not involve template parameters}}
+
+ using X = int(void_t<T*>);
+ // expected-error at -1 {{'void' as parameter must not involve template parameters}}
+ };
+ template<class T> void g(decltype((void)(T*)0));
+ // expected-error at -1 {{'void' as parameter must not involve template parameters}}
+#endif
+#if __cplusplus >= 202002L
+ template<class T> bool v = requires(void_t<T>) { true; };
+ // expected-error at -1 {{'void' as parameter must not involve template parameters}}
+#endif
+} // namespace cwg3179
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-cxx2c.cpp b/clang/test/CodeGenCXX/mangle-cxx2c.cpp
index 1dd5c7e4af264..fa0e464faeccc 100644
--- a/clang/test/CodeGenCXX/mangle-cxx2c.cpp
+++ b/clang/test/CodeGenCXX/mangle-cxx2c.cpp
@@ -14,13 +14,13 @@ template <int I, typename...V>
V...[I] bar() {return {};}
-template <int I, typename... T>
+template <typename... T>
using First = T...[0];
// CHECK-LABEL: define {{.*}} @_ZN8GH1120033bazILi0EJiEEEvDy_SUBSTPACK_Li0E
// FIXME: handle indexing of partially substituted packs
-template <int I, typename...V>
-void baz(First<I, int, V...>){};
+template <int, typename...V>
+void baz(First<int, V...>){};
void fn() {
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..3c266a9b091ae 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,47 @@ 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_E4typeE_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_E4typeE_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_E4typeIcEE_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_E4typeIcEE_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_E4typeE_ELi1EE
+ template void k<int*>(int);
+
+ struct impl6 {
+ template<class> struct type {};
+ };
+ template<typename T> using alias6 = impl6;
+ template<template <class> class> struct foo {};
+ template<typename T> void l(foo<alias6<T*>::type>) {}
+ // CHECK: @_ZN14type_qualifier1lIPiEEvNS_3fooINS_6alias6IPT_E4typeEEE
+ template void l<int*>(foo<impl6::type>);
}
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 1ede22eaf458a..5e6e43c89eb37 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()
@@ -1076,6 +1078,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 4db803564309b..20a46460b28a8 100644
--- a/clang/test/SemaCXX/typeof.cpp
+++ b/clang/test/SemaCXX/typeof.cpp
@@ -8,7 +8,7 @@ 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 01db2a54ae381..9dfd6853d0f9d 100644
--- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
+++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
@@ -517,15 +517,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
@@ -534,6 +534,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 6f7f00bf12e61..bb7a7a2ee8183 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1839,6 +1839,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 3715a496faa63..fb03f4866376a 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..684f06d2842e1 100644
--- a/clang/test/SemaTemplate/dependent-type-identity.cpp
+++ b/clang/test/SemaTemplate/dependent-type-identity.cpp
@@ -42,7 +42,7 @@ struct X1 {
void f3(X0<T>*); // expected-note{{previous}}
void f3(X0<U>*);
- void f3(::X0<type>*); // expected-error{{redeclar}}
+ void f3(::X0<type>*); // expected-error{{redeclar}}
void f4(typename T::template apply<U>*); // expected-note{{previous}}
void f4(typename U::template apply<U>*);
@@ -59,7 +59,7 @@ struct X1 {
void f6(typename N::X2<U>::template apply<U> *);
void f6(typename N::X2<U>::template apply<T> *);
void f6(typename ::N::X2<type>::template apply<U_type> *); // expected-error{{redeclar}}
-
+
void f7(typename N::X2<T>::template apply<U> *); // expected-note{{previous}}
void f7(typename N::X2<U>::template apply<U> *);
void f7(typename N::X2<U>::template apply<T> *);
@@ -148,9 +148,6 @@ 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/instantiate-scope.cpp b/clang/test/SemaTemplate/instantiate-scope.cpp
index 7dca971470e6a..2862643b8a7d1 100644
--- a/clang/test/SemaTemplate/instantiate-scope.cpp
+++ b/clang/test/SemaTemplate/instantiate-scope.cpp
@@ -8,12 +8,14 @@ template<typename ...T> struct X {
template<typename T, typename U> using A = T;
-// FIXME: These definitions are not OK, X<A<T, decltype(...)>...> is not equivalent to X<T...>.
+// These definitions are not OK, X<A<T, decltype(...)>...> is not equivalent to X<T...>.
template<typename ...T>
void X<A<T, decltype(f(T()))>...>::f(int) {}
+// expected-error at -1 {{nested name specifier 'X<A<T, decltype(f(T()))>...>' for declaration does not refer into a class}}
template<typename ...T>
-int X<A<T, decltype(f(T()))>...>::n = 0; // expected-error {{undeclared}}
+int X<A<T, decltype(f(T()))>...>::n = 0;
+// expected-error at -1 {{nested name specifier 'X<A<T, decltype(f(T()))>...>' for declaration does not refer into a class}}
struct Y {}; void f(Y);
@@ -22,7 +24,7 @@ void g() {
X<Y>().f(0);
X<Y>::n = 1;
- // FIXME: There should be no substitutiton failure since the out-of-line definitions were not valid.
+ // No substitutiton failure since the out-of-line definitions were not valid.
X<void>().f(0);
- X<void>::n = 1; // expected-note {{instantiation of}}
+ X<void>::n = 1;
}
diff --git a/clang/test/SemaTemplate/instantiation-dependence.cpp b/clang/test/SemaTemplate/instantiation-dependence.cpp
index 0c75e2552344b..fd383f9b36e1c 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 {
@@ -111,3 +106,324 @@ 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
+
+namespace SimpleAlias1 {
+ template<class T> void foo(void_t<T*>*) = delete;
+ template<class T> void foo(void_t<T>*) {}
+ template void foo<int&>(void*);
+} // namespace SimpleAlias1
+
+namespace OutOfLinePartial1 {
+ template<class> struct A {
+ template<class, class> struct B {};
+ template<class U> struct B<U, typename U::type1>;
+ template<class U> struct B<U, typename U::type2>;
+ };
+ template<> template<class U> struct A<int>::B<U, typename U::type1> {};
+ template<> template<class U> struct A<int>::B<U, typename U::type2> {};
+} // namespace OutOfLinePartial1
+
+// 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/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 79f75909c57b5..b1b4a0f96983f 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -278,7 +278,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);
@@ -294,7 +294,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);
@@ -310,7 +310,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/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index d1a260b07a0fa..f1440d2bfe241 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -13649,7 +13649,11 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/temp.alias">temp.alias</a>]</td>
<td>drafting</td>
<td>Equivalent but not functionally-equivalent redeclarations</td>
- <td align="center">Not resolved</td>
+ <td align="center">
+ <details>
+ <summary>Not resolved</summary>
+ Clang 23 implements 2014-10-01 resolution
+ </details></td>
</tr>
<tr id="1981">
<td><a href="https://cplusplus.github.io/CWG/issues/1981.html">1981</a></td>
@@ -14237,7 +14241,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/temp.type">temp.type</a>]</td>
<td>CD4</td>
<td>Conflicting specifications for dependent <I>decltype-specifier</I>s</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 23</td>
</tr>
<tr id="2065">
<td><a href="https://cplusplus.github.io/CWG/issues/2065.html">2065</a></td>
@@ -22070,7 +22074,11 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/dcl.fct">dcl.fct</a>]</td>
<td>ready</td>
<td>More edge cases for a <TT>void</TT> function parameter</td>
- <td align="center">Not resolved</td>
+ <td align="center">
+ <details>
+ <summary>Not resolved</summary>
+ Clang 23 implements 2026-04-30 resolution
+ </details></td>
</tr>
<tr class="open" id="3180">
<td><a href="https://cplusplus.github.io/CWG/issues/3180.html">3180</a></td>
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