[clang] [clang] implement CWG2064: ignore value dependence for decltype (PR #190495)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 9 13:42:30 PDT 2026
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/190495
>From 6010425dbbc455ba3bbc10b0ea151799724406d6 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.
Readds a few test cases from da98651
Fixes #8740
Fixes #61818
Fixes #190388
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/AST/ASTContext.h | 48 +-
clang/include/clang/AST/DependenceFlags.h | 8 +-
clang/include/clang/AST/TypeBase.h | 3 +-
clang/lib/AST/ASTContext.cpp | 517 ++++++++++++++++--
clang/lib/AST/ASTDiagnostic.cpp | 2 +-
clang/lib/AST/DeclTemplate.cpp | 4 +-
clang/lib/AST/ItaniumMangle.cpp | 15 +-
clang/lib/AST/Type.cpp | 41 +-
clang/lib/Sema/SemaOverload.cpp | 4 +-
clang/lib/Sema/SemaTemplate.cpp | 44 +-
clang/test/CXX/drs/cwg20xx.cpp | 17 +-
.../test/CXX/temp/temp.decls/temp.mem/p5.cpp | 2 +-
clang/test/CodeGenCXX/GH190495.cpp | 29 +
clang/test/CodeGenCXX/mangle-subst.cpp | 13 +-
clang/test/Sema/invalid-bitwidth-expr.mm | 1 +
clang/test/SemaCXX/decltype.cpp | 11 +-
clang/test/SemaCXX/source_location.cpp | 7 +-
clang/test/SemaCXX/typeof.cpp | 2 +-
.../SemaTemplate/concepts-out-of-line-def.cpp | 6 +-
clang/test/SemaTemplate/concepts.cpp | 9 +
clang/test/SemaTemplate/deduction-guide.cpp | 23 +-
clang/test/SemaTemplate/dependent-expr.cpp | 8 +-
.../test/SemaTemplate/injected-class-name.cpp | 44 ++
.../SemaTemplate/instantiation-dependence.cpp | 90 ++-
.../SemaTemplate/partial-spec-instantiate.cpp | 7 +
.../SemaTemplate/temp_arg_template_p0522.cpp | 11 +-
27 files changed, 835 insertions(+), 132 deletions(-)
create mode 100644 clang/test/CodeGenCXX/GH190495.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2fe76e60946f5..10996aefcd59b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -423,6 +423,7 @@ Bug Fixes to C++ Support
- Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307)
- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable
when used inside decltype in the return type. (#GH180460)
+- `decltype(expr)` now ignores value-dependence on the expression. (#GH61818)
- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044)
- Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. (#GH183887)
- Fixed a crash on ``typeid`` of incomplete local types during template instantiation. (#GH63242), (#GH176397)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index ba1b58489c327..2a827376047f9 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -401,6 +401,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// current file before they are compared locally.
unsigned NextStringLiteralVersion = 0;
+ /// A cache mapping from types to their functionally equivalent canonical
+ /// type.
+ mutable llvm::DenseMap<const Type *, QualType>
+ FunctionallyEquivalentTypeCache;
+
/// MD5 hash of CUID. It is calculated when first used and cached by this
/// data member.
mutable std::string CUIDHash;
@@ -1975,9 +1980,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
Decl *AssociatedDecl, unsigned Index,
UnsignedOrNone PackIndex,
bool Final) const;
- QualType getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
- unsigned Index, bool Final,
- const TemplateArgument &ArgPack);
+ QualType
+ getSubstTemplateTypeParmPackType(Decl *AssociatedDecl, unsigned Index,
+ bool Final,
+ const TemplateArgument &ArgPack) const;
QualType getSubstBuiltinTemplatePack(const TemplateArgument &ArgPack);
QualType
@@ -1993,7 +1999,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
ArrayRef<TemplateArgument> SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs,
- QualType Underlying = QualType()) const;
+ QualType Underlying = QualType(),
+ bool Unique = false) const;
QualType
getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
@@ -2964,6 +2971,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
return getCanonicalType(T1) == getCanonicalType(T2);
}
+ QualType getCanonicalType(QualType QT, bool FunctionallyEquivalent) const;
+ QualType getCanonicalType(QualType QT, bool FunctionallyEquivalent,
+ bool &AnyNonCanonical) const {
+ QualType R = getCanonicalType(QT, FunctionallyEquivalent);
+ AnyNonCanonical |= !R.isCanonical();
+ return R;
+ }
+ bool hasFunctionallyEquivalentType(QualType T1, QualType T2) const;
+
/// Determine whether the given expressions \p X and \p Y are equivalent.
bool hasSameExpr(const Expr *X, const Expr *Y) const;
@@ -3118,14 +3134,30 @@ 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,
+ bool FunctionallyEquivalent,
+ bool &AnyNonCanonical) const;
+ TemplateArgument
+ getCanonicalTemplateArgument(const TemplateArgument &Arg,
+ bool FunctionallyEquivalent = false) const {
+ bool AnyNonCanonical = false;
+ return getCanonicalTemplateArgument(Arg, FunctionallyEquivalent,
+ AnyNonCanonical);
+ }
/// Canonicalize the given template argument list.
///
/// Returns true if any arguments were non-canonical, false otherwise.
+ bool canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args,
+ bool FunctionallyEquivalent,
+ bool &AnyNonCanonical) const;
bool
- canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args) const;
+ canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args,
+ bool FunctionallyEquivalent = false) const {
+ bool AnyNonCanonical = false;
+ return canonicalizeTemplateArguments(Args, FunctionallyEquivalent,
+ AnyNonCanonical);
+ }
/// Canonicalize the given TemplateTemplateParmDecl.
TemplateTemplateParmDecl *
@@ -3268,6 +3300,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
// Helper for integer ordering
unsigned getIntegerRank(const Type *T) const;
+ QualType buildFunctionallyEquivalentCanonicalType(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/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index 8802b15d99034..cc561dc1fe424 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -7496,7 +7496,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/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ee7f823b014b2..65d53ea30e2d5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3172,21 +3172,34 @@ ASTContext::getASTObjCInterfaceLayout(const ObjCInterfaceDecl *D) const {
static auto getCanonicalTemplateArguments(const ASTContext &C,
ArrayRef<TemplateArgument> Args,
- bool &AnyNonCanonArgs) {
+ bool &AnyChanged,
+ bool FunctionallyEquivalent,
+ bool &AnyNonCanonical) {
SmallVector<TemplateArgument, 16> CanonArgs(Args);
- AnyNonCanonArgs |= C.canonicalizeTemplateArguments(CanonArgs);
+ AnyChanged |= C.canonicalizeTemplateArguments(
+ CanonArgs, FunctionallyEquivalent, AnyNonCanonical);
return CanonArgs;
}
+static auto getCanonicalTemplateArguments(const ASTContext &C,
+ ArrayRef<TemplateArgument> Args,
+ bool &AnyChanged) {
+ bool AnyNonCanonical = false;
+ return getCanonicalTemplateArguments(
+ C, Args, AnyChanged, /*FunctionallyEquivalent=*/false, AnyNonCanonical);
+}
+
bool ASTContext::canonicalizeTemplateArguments(
- MutableArrayRef<TemplateArgument> Args) const {
- bool AnyNonCanonArgs = false;
+ MutableArrayRef<TemplateArgument> Args, bool FunctionallyEquivalent,
+ bool &AnyNonCanonical) const {
+ bool AnyChanged = false;
for (auto &Arg : Args) {
TemplateArgument OrigArg = Arg;
- Arg = getCanonicalTemplateArgument(Arg);
- AnyNonCanonArgs |= !Arg.structurallyEquals(OrigArg);
+ Arg = getCanonicalTemplateArgument(Arg, FunctionallyEquivalent,
+ AnyNonCanonical);
+ AnyChanged |= !Arg.structurallyEquals(OrigArg);
}
- return AnyNonCanonArgs;
+ return AnyChanged;
}
//===----------------------------------------------------------------------===//
@@ -5861,10 +5874,9 @@ QualType ASTContext::getSubstTemplateTypeParmType(QualType Replacement,
return QualType(SubstParm, 0);
}
-QualType
-ASTContext::getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
- unsigned Index, bool Final,
- const TemplateArgument &ArgPack) {
+QualType ASTContext::getSubstTemplateTypeParmPackType(
+ Decl *AssociatedDecl, unsigned Index, bool Final,
+ const TemplateArgument &ArgPack) const {
#ifndef NDEBUG
for (const auto &P : ArgPack.pack_elements())
assert(P.getKind() == TemplateArgument::Type && "Pack contains a non-type");
@@ -6041,8 +6053,8 @@ QualType ASTContext::getCanonicalTemplateSpecializationType(
#endif
llvm::FoldingSetNodeID ID;
- TemplateSpecializationType::Profile(ID, Keyword, Template, Args, QualType(),
- *this);
+ TemplateSpecializationType::Profile(ID, Keyword, Template, Args,
+ /*IsTypeAlias=*/false, QualType(), *this);
void *InsertPos = nullptr;
if (auto *T = TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(T, 0);
@@ -6063,9 +6075,27 @@ QualType ASTContext::getCanonicalTemplateSpecializationType(
QualType ASTContext::getTemplateSpecializationType(
ElaboratedTypeKeyword Keyword, TemplateName Template,
ArrayRef<TemplateArgument> SpecifiedArgs,
- ArrayRef<TemplateArgument> CanonicalArgs, QualType Underlying) const {
- const auto *TD = Template.getAsTemplateDecl(/*IgnoreDeduced=*/true);
- bool IsTypeAlias = TD && TD->isTypeAlias();
+ ArrayRef<TemplateArgument> CanonicalArgs, QualType Underlying,
+ bool Unique) const {
+
+ bool IsTypeAlias = false;
+ if (!Underlying.isNull()) {
+ const auto *TD = Template.getAsTemplateDecl(/*IgnoreDeduced=*/true);
+ IsTypeAlias = TD && TD->isTypeAlias();
+ if (!IsTypeAlias)
+ Underlying = getCanonicalType(Underlying);
+ }
+
+ llvm::FoldingSetNodeID ID;
+ void *InsertPos = nullptr;
+ if (Unique) {
+ TemplateSpecializationType::Profile(ID, Keyword, Template, SpecifiedArgs,
+ IsTypeAlias, Underlying, *this);
+ if (auto *T =
+ TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos))
+ return QualType(T, 0);
+ }
+
if (Underlying.isNull()) {
TemplateName CanonTemplate =
getCanonicalTemplateName(Template, /*IgnoreDeduced=*/true);
@@ -6087,18 +6117,16 @@ QualType ASTContext::getTemplateSpecializationType(
});
}
- // We can get here with an alias template when the specialization
- // contains a pack expansion that does not match up with a parameter
- // pack, or a builtin template which cannot be resolved due to dependency.
- assert((!isa_and_nonnull<TypeAliasTemplateDecl>(TD) ||
- hasAnyPackExpansions(CanonicalArgs)) &&
- "Caller must compute aliased type");
- IsTypeAlias = false;
-
Underlying = getCanonicalTemplateSpecializationType(
CanonKeyword, CanonTemplate, CanonicalArgs);
if (!NonCanonical)
return Underlying;
+
+ if (Unique) {
+ [[maybe_unused]] auto *T =
+ TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos);
+ assert(!T && "broken canonical type");
+ }
}
void *Mem = Allocate(sizeof(TemplateSpecializationType) +
sizeof(TemplateArgument) * SpecifiedArgs.size() +
@@ -6107,6 +6135,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);
}
@@ -6691,12 +6721,11 @@ QualType ASTContext::getReferenceQualifiedType(const Expr *E) const {
/// 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).
+ // 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->isInstantiationDependent()) {
+ if (!E->isTypeDependent()) {
CanonType = getCanonicalType(UnderlyingType);
} else if (!UnderlyingType.isNull()) {
CanonType = getDecltypeType(E, QualType());
@@ -7888,51 +7917,66 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
}
TemplateArgument
-ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
+ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg,
+ bool FunctionallyEquivalent,
+ bool &AnyNonCanonical) const {
switch (Arg.getKind()) {
case TemplateArgument::Null:
return Arg;
case TemplateArgument::Expression:
+ // FIXME: Do functionally equivalent expressions need different profiling?
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::Declaration:
+ return TemplateArgument(
+ cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl()),
+ getCanonicalType(Arg.getParamTypeForDecl(), FunctionallyEquivalent,
+ AnyNonCanonical),
+ Arg.getIsDefaulted());
case TemplateArgument::NullPtr:
- return TemplateArgument(getCanonicalType(Arg.getNullPtrType()),
- /*isNullPtr*/ true, Arg.getIsDefaulted());
+ return TemplateArgument(getCanonicalType(Arg.getNullPtrType(),
+ FunctionallyEquivalent,
+ AnyNonCanonical),
+ /*isNullPtr=*/true, Arg.getIsDefaulted());
case TemplateArgument::Template:
+ // FIXME: Implement functional canonicalization of template names.
return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()),
Arg.getIsDefaulted());
case TemplateArgument::TemplateExpansion:
+ // FIXME: Implement functional canonicalization of template names.
return TemplateArgument(
getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern()),
Arg.getNumTemplateExpansions(), Arg.getIsDefaulted());
case TemplateArgument::Integral:
- return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType()));
+ return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType(),
+ FunctionallyEquivalent,
+ AnyNonCanonical));
case TemplateArgument::StructuralValue:
return TemplateArgument(*this,
- getCanonicalType(Arg.getStructuralValueType()),
+ getCanonicalType(Arg.getStructuralValueType(),
+ FunctionallyEquivalent,
+ AnyNonCanonical),
Arg.getAsStructuralValue(), Arg.getIsDefaulted());
case TemplateArgument::Type:
- return TemplateArgument(getCanonicalType(Arg.getAsType()),
- /*isNullPtr*/ false, Arg.getIsDefaulted());
+ return TemplateArgument(getCanonicalType(Arg.getAsType(),
+ FunctionallyEquivalent,
+ 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, FunctionallyEquivalent,
+ AnyNonCanonical);
+ if (!AnyChanged)
return Arg;
auto NewArg = TemplateArgument::CreatePackCopy(
const_cast<ASTContext &>(*this), CanonArgs);
@@ -13858,6 +13902,391 @@ unsigned ASTContext::getTargetAddressSpace(LangAS AS) const {
return getTargetInfo().getTargetAddressSpace(AS);
}
+static NestedNameSpecifier
+getFunctionallyEquivalentCanonicalQualifier(const ASTContext &Context,
+ NestedNameSpecifier Qualifier,
+ bool &AnyNonCanonical) {
+ if (Qualifier.getKind() == NestedNameSpecifier::Kind::Type)
+ return NestedNameSpecifier(
+ Context
+ .getCanonicalType(QualType(Qualifier.getAsType(), 0),
+ /*FunctionallyEquivalent=*/true, AnyNonCanonical)
+ .getTypePtr());
+ return Qualifier.getCanonical();
+}
+
+QualType ASTContext::getCanonicalType(QualType QT,
+ bool FunctionallyEquivalent) const {
+ if (!FunctionallyEquivalent)
+ return QT.getCanonicalType();
+
+ // A canonical type is functionally equivalent to itself.
+ if (QT.isCanonical())
+ return QT;
+
+ // A non-instantiation-dependent type is functionally equivalent to its
+ // canonical type.
+ if (!QT->isInstantiationDependentType())
+ return getCanonicalType(QT);
+ auto [T, Qualifiers] = QT.split();
+
+ auto It = FunctionallyEquivalentTypeCache.find(T);
+ if (It != FunctionallyEquivalentTypeCache.end())
+ return It->second;
+
+ QualType R = buildFunctionallyEquivalentCanonicalType(T);
+ assert(hasSameType(QualType(T, 0), R));
+
+ auto [_, Inserted] = FunctionallyEquivalentTypeCache.try_emplace(T, R);
+ assert(Inserted && "Unexpected cache entry for type");
+
+ return getQualifiedType(R, Qualifiers);
+}
+
+QualType
+ASTContext::buildFunctionallyEquivalentCanonicalType(const Type *T) const {
+ assert(!T->isCanonicalUnqualified());
+ assert(T->isInstantiationDependentType());
+
+ // If none of the inputs became non-canonical, just return the canonical type.
+ // It's not helpful for the applications of this transform to track whether
+ // the inputs changed, because they will mostly refer to template parameters
+ // that haven't been canonicalized.
+ bool AnyNonCanonical = false;
+ switch (T->getTypeClass()) {
+ case Type::Builtin:
+ llvm_unreachable(
+ "always canonical types should have been handled by this point");
+ case Type::PredefinedSugar:
+ llvm_unreachable("never-instantation-dependent types should have been "
+ "handled by this point");
+ case Type::Complex:
+ case Type::FunctionNoProto:
+ case Type::Adjusted:
+ case Type::Decayed:
+ case Type::Auto:
+ case Type::DeducedTemplateSpecialization:
+ case Type::ObjCObject:
+ case Type::ObjCInterface:
+ case Type::ObjCObjectPointer:
+ case Type::Atomic:
+ case Type::Record:
+ case Type::Enum:
+ 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::MacroQualified:
+ case Type::ObjCTypeParam:
+ case Type::Pipe:
+ case Type::TypeOf:
+ case Type::TypeOfExpr:
+ case Type::Using:
+ case Type::DependentBitInt:
+ case Type::OverflowBehavior:
+ case Type::SubstBuiltinTemplatePack:
+ case Type::UnresolvedUsing:
+ T->dump();
+ llvm_unreachable("unimplemented");
+ case Type::FunctionProto: {
+ const auto *TT = cast<FunctionProtoType>(T);
+ QualType RT = getCanonicalType(
+ TT->getReturnType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ SmallVector<QualType, 8> PTs(TT->getParamTypes().size());
+ llvm::transform(TT->getParamTypes(), PTs.begin(), [&](QualType PT) {
+ return getCanonicalType(PT, /*FunctionallyEquivalent=*/true,
+ AnyNonCanonical);
+ });
+ if (!AnyNonCanonical)
+ break;
+ return getFunctionType(RT, PTs, TT->getExtProtoInfo());
+ }
+ case Type::Pointer: {
+ const auto *TT = cast<PointerType>(T);
+ QualType Pointee = getCanonicalType(
+ TT->getPointeeType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getPointerType(Pointee);
+ }
+ case Type::LValueReference: {
+ const auto *TT = cast<LValueReferenceType>(T);
+ QualType Pointee = getCanonicalType(
+ TT->getPointeeType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getLValueReferenceType(Pointee);
+ }
+ case Type::RValueReference: {
+ const auto *TT = cast<RValueReferenceType>(T);
+ QualType Pointee = getCanonicalType(
+ TT->getPointeeType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getRValueReferenceType(Pointee);
+ }
+ case Type::InjectedClassName: {
+ const auto *TT = cast<InjectedClassNameType>(T);
+
+ ElaboratedTypeKeyword Keyword =
+ ::getCanonicalElaboratedTypeKeyword(TT->getKeyword());
+ NestedNameSpecifier Qualifier =
+ ::getFunctionallyEquivalentCanonicalQualifier(*this, TT->getQualifier(),
+ AnyNonCanonical);
+ const TagDecl *ND = TT->getDecl()->getCanonicalDecl();
+ if (!AnyNonCanonical)
+ break;
+ return getTagType(Keyword, Qualifier, ND, /*OwnsTag=*/false);
+ }
+ case Type::Attributed:
+ return getCanonicalType(cast<AttributedType>(T)->desugar(),
+ /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ case Type::Paren:
+ return getCanonicalType(cast<ParenType>(T)->desugar(),
+ /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ case Type::PackExpansion: {
+ const auto *TT = cast<PackExpansionType>(T);
+ QualType Pattern = getCanonicalType(
+ TT->getPattern(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getPackExpansionType(Pattern, TT->getNumExpansions(),
+ /*ExpectPackInType=*/false);
+ }
+ case Type::PackIndexing: {
+ const auto *TT = cast<PackIndexingType>(T);
+
+ // This type holds on to instantiation-dependence.
+ assert(!TT->isSugared());
+
+ QualType Pattern = getCanonicalType(
+ TT->getPattern(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ SmallVector<QualType, 8> Expansions(TT->getExpansions().size());
+ llvm::transform(TT->getExpansions(), Expansions.begin(), [&](QualType E) {
+ return getCanonicalType(E, /*FunctionallyEquivalent=*/true,
+ AnyNonCanonical);
+ });
+ if (!AnyNonCanonical)
+ break;
+ return getPackIndexingType(Pattern, TT->getIndexExpr(),
+ TT->isFullySubstituted(), Expansions,
+ TT->getSelectedIndex());
+ }
+ case Type::UnaryTransform:
+ // FIXME: Unimplemented. These appear to have broken canonicalization.
+ break;
+ case Type::MemberPointer: {
+ const auto *TT = cast<MemberPointerType>(T);
+ const auto *Cls = TT->getMostRecentCXXRecordDecl();
+ NestedNameSpecifier Qualifier =
+ ::getFunctionallyEquivalentCanonicalQualifier(*this, TT->getQualifier(),
+ 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(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getMemberPointerType(Pointee, Qualifier, Cls);
+ }
+ case Type::BlockPointer: {
+ const auto *TT = cast<BlockPointerType>(T);
+ QualType Pointee = getCanonicalType(
+ TT->getPointeeType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getBlockPointerType(Pointee);
+ }
+ case Type::IncompleteArray: {
+ const auto *TT = cast<IncompleteArrayType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getIncompleteArrayType(ElementType, TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers());
+ }
+ case Type::ConstantArray: {
+ const auto *TT = cast<ConstantArrayType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getConstantArrayType(ElementType, TT->getSize(), TT->getSizeExpr(),
+ TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers());
+ }
+ case Type::VariableArray: {
+ const auto *TT = cast<VariableArrayType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getVariableArrayType(ElementType, TT->getSizeExpr(),
+ TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers());
+ }
+ case Type::DependentSizedArray: {
+ const auto *TT = cast<DependentSizedArrayType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentSizedArrayType(ElementType, TT->getSizeExpr(),
+ TT->getSizeModifier(),
+ TT->getIndexTypeCVRQualifiers());
+ }
+ case Type::ExtVector: {
+ const auto *TT = cast<ExtVectorType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getExtVectorType(ElementType, TT->getNumElements());
+ }
+ case Type::DependentSizedExtVector: {
+ const auto *TT = cast<DependentSizedExtVectorType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentSizedExtVectorType(ElementType, TT->getSizeExpr(),
+ TT->getAttributeLoc());
+ }
+ case Type::ConstantMatrix: {
+ const auto *TT = cast<ConstantMatrixType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getConstantMatrixType(ElementType, TT->getNumRows(),
+ TT->getNumColumns());
+ }
+ case Type::DependentSizedMatrix: {
+ const auto *TT = cast<DependentSizedMatrixType>(T);
+ QualType ElementType = getCanonicalType(
+ TT->getElementType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentSizedMatrixType(ElementType, TT->getRowExpr(),
+ TT->getColumnExpr(),
+ TT->getAttributeLoc());
+ }
+ case Type::DependentAddressSpace: {
+ const auto *TT = cast<DependentAddressSpaceType>(T);
+ QualType Pointee = getCanonicalType(
+ TT->getPointeeType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentAddressSpaceType(Pointee, TT->getAddrSpaceExpr(),
+ TT->getAttributeLoc());
+ }
+ case Type::SubstTemplateTypeParmPack: {
+ const auto *TT = cast<SubstTemplateTypeParmPackType>(T);
+ TemplateArgument ArgPack = getCanonicalTemplateArgument(
+ TT->getArgumentPack(), /*FunctionallyEquivalent=*/true,
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getSubstTemplateTypeParmPackType(
+ TT->getAssociatedDecl()->getCanonicalDecl(), TT->getIndex(),
+ TT->getFinal(), ArgPack);
+ }
+ case Type::DependentName: {
+ const auto *TT = cast<DependentNameType>(T);
+ ElaboratedTypeKeyword Keyword =
+ ::getCanonicalElaboratedTypeKeyword(TT->getKeyword());
+ NestedNameSpecifier Qualifier =
+ ::getFunctionallyEquivalentCanonicalQualifier(*this, TT->getQualifier(),
+ AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getDependentNameType(Keyword, Qualifier, TT->getIdentifier());
+ }
+ case Type::TemplateSpecialization: {
+ const auto *TT = cast<TemplateSpecializationType>(T);
+ // For a type alias, we want to keep it only if there are unused
+ // instantiation-dependent template arguments.
+ if (TT->isTypeAlias()) {
+ const TemplateDecl *TD = TT->getTemplateName().getAsTemplateDecl();
+ auto As = TT->template_arguments();
+ bool AnyUnusedInstantiationDependentArgs = false;
+ 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()) {
+ auto Dep = TemplateArgumentDependence::None;
+ for (const auto &A : CurAs)
+ Dep |= A.getDependence();
+ if (Dep & TemplateArgumentDependence::Instantiation) {
+ AnyUnusedInstantiationDependentArgs = true;
+ break;
+ }
+ }
+ As = As.drop_front(CurAs.size());
+ }
+ if (!AnyUnusedInstantiationDependentArgs)
+ return getCanonicalType(TT->getAliasedType(),
+ /*FunctionallyEquivalent=*/true,
+ AnyNonCanonical);
+ // Otherwise, we are keeping the type alias. This on itself makes the
+ // resulting type non-canonical.
+ AnyNonCanonical = true;
+ }
+ ElaboratedTypeKeyword Keyword =
+ ::getCanonicalElaboratedTypeKeyword(TT->getKeyword());
+ // FIXME: Implement getFunctionallyEquivalentCanonicalTemplateName and use
+ // it here.
+ TemplateName TN =
+ getCanonicalTemplateName(TT->getTemplateName(), /*IgnoreDeduced=*/true);
+ // FIXME: We can't avoid rebuilding if nothing changed, because we can't
+ // rely on this TST to have been uniqued.
+ bool AnyChanged = false;
+ auto As = ::getCanonicalTemplateArguments(
+ *this, TT->template_arguments(), AnyChanged,
+ /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+ if (!AnyNonCanonical)
+ break;
+ return getTemplateSpecializationType(
+ Keyword, TN, As,
+ /*CanonicalArgs=*/ArrayRef<TemplateArgument>(),
+ TT->desugar().getCanonicalType(),
+ /*Unique=*/true);
+ }
+ case Type::Decltype: {
+ const auto *TT = cast<DecltypeType>(T);
+ AnyNonCanonical |= TT->isSugared();
+ if (!AnyNonCanonical)
+ break;
+ return QualType(TT, 0);
+ }
+ // These types don't have other types in their spelling, so they always
+ // canonicalize to the equivalent form.
+ case Type::Typedef:
+ case Type::SubstTemplateTypeParm:
+ case Type::TemplateTypeParm:
+ break;
+ }
+ return T->getCanonicalTypeInternal();
+}
+
+bool ASTContext::hasFunctionallyEquivalentType(QualType T1, QualType T2) const {
+ return hasSameType(T1, T2) &&
+ getCanonicalType(T1, /*FunctionallyEquivalent=*/true) ==
+ getCanonicalType(T2, /*FunctionallyEquivalent=*/true);
+}
+
bool ASTContext::hasSameExpr(const Expr *X, const Expr *Y) const {
if (X == Y)
return true;
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index b8023cb6fa10f..9f79635ad4c54 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -126,7 +126,7 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT,
ShouldAKA = true;
QT = Context.getTemplateSpecializationType(
TST->getKeyword(), TST->getTemplateName(), Args,
- /*CanonicalArgs=*/{}, QT);
+ /*CanonicalArgs=*/{}, QT.getCanonicalType());
}
break;
}
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 99d02fdc99e92..29c10c45e62da 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1202,11 +1202,13 @@ CanQualType
ClassTemplatePartialSpecializationDecl::getCanonicalInjectedSpecializationType(
const ASTContext &Ctx) const {
if (CanonInjectedTST.isNull()) {
+ SmallVector<TemplateArgument, 4> CanonicalArgs(getTemplateArgs().asArray());
+ Ctx.canonicalizeTemplateArguments(CanonicalArgs);
CanonInjectedTST =
CanQualType::CreateUnsafe(Ctx.getCanonicalTemplateSpecializationType(
ElaboratedTypeKeyword::None,
TemplateName(getSpecializedTemplate()->getCanonicalDecl()),
- getTemplateArgs().asArray()));
+ CanonicalArgs));
}
return CanonInjectedTST;
}
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index d21faaa87558d..54f0993f80186 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -3008,6 +3008,11 @@ void CXXNameMangler::mangleType(QualType T) {
if (!TST->isTypeAlias())
break;
+ // instantiation-dependent decltypes are mangled through their
+ // expressions.
+ if (T->isInstantiationDependentType() && isa<DecltypeType>(T))
+ break;
+
// FIXME: We presumably shouldn't strip off ElaboratedTypes with
// instantation-dependent qualifiers. See
// https://github.com/itanium-cxx-abi/cxx-abi/issues/114.
@@ -7005,8 +7010,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());
@@ -7175,8 +7181,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/Type.cpp b/clang/lib/AST/Type.cpp
index 78983fd38410d..992e72c8e8e14 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -428,9 +428,16 @@ BitIntType::BitIntType(bool IsUnsigned, unsigned NumBits)
NumBits(NumBits) {}
DependentBitIntType::DependentBitIntType(bool IsUnsigned, Expr *NumBitsExpr)
+ // DependentBitIntType must always be type-dependent.
+ // The expression must be value-dependent, so will also be
+ // instantiation-dependent.
: Type(DependentBitInt, QualType{},
- toTypeDependence(NumBitsExpr->getDependence())),
- ExprAndUnsigned(NumBitsExpr, IsUnsigned) {}
+ toTypeDependence(NumBitsExpr->getDependence()) |
+ TypeDependence::Dependent),
+ ExprAndUnsigned(NumBitsExpr, IsUnsigned) {
+ assert(NumBitsExpr->isValueDependent() &&
+ "NumBitsExpr must be value-dependent");
+}
bool DependentBitIntType::isUnsigned() const {
return ExprAndUnsigned.getInt();
@@ -4219,15 +4226,10 @@ DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
// 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, can, toTypeDependence(E->getDependence())), E(E),
+ UnderlyingType(underlyingType) {}
-bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); }
+bool DecltypeType::isSugared() const { return !E->isTypeDependent(); }
QualType DecltypeType::desugar() const {
if (isSugared())
@@ -4711,23 +4713,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/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 11e771bc240f1..0c26e5f5b1426 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1409,7 +1409,7 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
bool SameTemplateParameterList = SemaRef.TemplateParameterListsAreEqual(
NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
OldTemplate->getTemplateParameters(), false, Sema::TPL_TemplateMatch);
- bool SameReturnType = SemaRef.Context.hasSameType(
+ bool SameReturnType = SemaRef.Context.hasFunctionallyEquivalentType(
Old->getDeclaredReturnType(), New->getDeclaredReturnType());
// FIXME(GH58571): Match template parameter list even for non-constrained
// template heads. This currently ensures that the code prior to C++20 is
@@ -3583,7 +3583,7 @@ bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
QualType NewType =
Context.removePtrSizeAddrSpace((New.begin() + J)->getUnqualifiedType());
- if (!Context.hasSameType(OldType, NewType)) {
+ if (!Context.hasFunctionallyEquivalentType(OldType, NewType)) {
if (ArgPos)
*ArgPos = Idx;
return false;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index aa72cb8fa2895..ea11aa64c60bf 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3556,7 +3556,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);
}
@@ -8904,14 +8910,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;
@@ -8919,15 +8925,20 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
}
}
+ SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted =
+ CTAI.SugaredConverted;
+ Context.canonicalizeTemplateArguments(FunctionallyEquivalentConverted,
+ /*FunctionallyEquivalent=*/true);
+
void *InsertPos = nullptr;
ClassTemplateSpecializationDecl *PrevDecl = nullptr;
if (isPartialSpecialization)
PrevDecl = ClassTemplate->findPartialSpecialization(
- CTAI.CanonicalConverted, TemplateParams, InsertPos);
+ FunctionallyEquivalentConverted, TemplateParams, InsertPos);
else
- PrevDecl =
- ClassTemplate->findSpecialization(CTAI.CanonicalConverted, InsertPos);
+ PrevDecl = ClassTemplate->findSpecialization(
+ FunctionallyEquivalentConverted, InsertPos);
ClassTemplateSpecializationDecl *Specialization = nullptr;
@@ -8944,7 +8955,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
// this explicit specialization or friend declaration.
Specialization = ClassTemplateSpecializationDecl::Create(
Context, Kind, ClassTemplate->getDeclContext(), KWLoc, TemplateNameLoc,
- ClassTemplate, CTAI.CanonicalConverted, CTAI.StrictPackMatch, PrevDecl);
+ ClassTemplate, FunctionallyEquivalentConverted, CTAI.StrictPackMatch,
+ PrevDecl);
Specialization->setTemplateArgsAsWritten(TemplateArgs);
SetNestedNameSpecifier(*this, Specialization, SS);
if (TemplateParameterLists.size() > 0) {
@@ -8955,14 +8967,13 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
if (!PrevDecl)
ClassTemplate->AddSpecialization(Specialization, InsertPos);
} else {
- CanQualType CanonType = CanQualType::CreateUnsafe(
- Context.getCanonicalTemplateSpecializationType(
- ElaboratedTypeKeyword::None,
- TemplateName(ClassTemplate->getCanonicalDecl()),
- CTAI.CanonicalConverted));
- if (Context.hasSameType(
- CanonType,
- ClassTemplate->getCanonicalInjectedSpecializationType(Context)) &&
+ QualType CanonType = Context.getTemplateSpecializationType(
+ ElaboratedTypeKeyword::None,
+ TemplateName(ClassTemplate->getCanonicalDecl()),
+ FunctionallyEquivalentConverted, /*CanonicalArgs=*/{},
+ /*Underlying=*/QualType(), /*Unique=*/true);
+ if (CanonType ==
+ ClassTemplate->getCanonicalInjectedSpecializationType(Context) &&
(!Context.getLangOpts().CPlusPlus20 ||
!TemplateParams->hasAssociatedConstraints())) {
// C++ [temp.class.spec]p9b3:
@@ -8989,7 +9000,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
ClassTemplatePartialSpecializationDecl *Partial =
ClassTemplatePartialSpecializationDecl::Create(
Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
- ClassTemplate, CTAI.CanonicalConverted, CanonType, PrevPartial);
+ ClassTemplate, FunctionallyEquivalentConverted,
+ Context.getCanonicalType(CanonType), PrevPartial);
Partial->setTemplateArgsAsWritten(TemplateArgs);
SetNestedNameSpecifier(*this, Partial, SS);
if (TemplateParameterLists.size() > 1 && SS.isSet()) {
diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp
index 75b4094283db0..f669d460f4d09 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 {
@@ -86,7 +99,7 @@ namespace cwg2076 { // cwg2076: 13
operator string_view() const;
};
- void foo(const string &); // #cwg2076-foo
+ void foo(const string &); // #cwg2076-foo
void bar(string_view); // #cwg2076-bar
void func(const string &arg) {
@@ -369,7 +382,7 @@ int f()
return 0;
}
} // namespace GH42233
-} // namespace cwg2091
+} // namespace cwg2091
namespace cwg2094 { // cwg2094: 5
struct A { int n; };
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/GH190495.cpp b/clang/test/CodeGenCXX/GH190495.cpp
new file mode 100644
index 0000000000000..87984f8c34ac1
--- /dev/null
+++ b/clang/test/CodeGenCXX/GH190495.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++26 -emit-llvm %s -o - -triple=x86_64-apple-darwin9
+
+namespace std {
+class partial_ordering {};
+class strong_ordering {
+public:
+ operator partial_ordering();
+};
+using size_t = decltype(sizeof(int));
+template <size_t> struct __priority_tag {};
+namespace __partial_order {
+struct __fn {
+ template <class _Tp, class _Up>
+ constexpr auto __go(_Tp __t, _Up __u, __priority_tag<2>)
+ -> decltype(partial_ordering(partial_order(__t, __u))) {}
+ template <class _Tp, class _Up> auto operator()(_Tp __t, _Up __u) {
+ __go(__t, __u, __priority_tag<2>());
+ }
+};
+}
+auto partial_order = __partial_order::__fn{};
+namespace {
+struct A {};
+strong_ordering partial_order(A, A) {
+ A a;
+ std::partial_order(a, a);
+}
+}
+}
diff --git a/clang/test/CodeGenCXX/mangle-subst.cpp b/clang/test/CodeGenCXX/mangle-subst.cpp
index 524e0febe479a..fa59ca2dfb3d0 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,14 @@ struct Inst : public A::Impl<A::Wrap> {};
void Test() { Inst a; }
}
+
+namespace InstantiationDependentDecltype {
+ struct a { a(char); };
+ struct b { a c(); };
+ // FIXME: This mangling is incorrect; the second decltype type should be a
+ // substitution for the first.
+ // CHECK: @_ZN30InstantiationDependentDecltype1fINS_1bEEEvDTcvNS_1aEcldtcvT__E1cEEDTcvS2_cldtcvS3__E1cEES3_S3_S2_S2_
+ // FIXME: @_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/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/decltype.cpp b/clang/test/SemaCXX/decltype.cpp
index 45a4c4cf1ac86..004d48415b0e7 100644
--- a/clang/test/SemaCXX/decltype.cpp
+++ b/clang/test/SemaCXX/decltype.cpp
@@ -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 value_dependent {
+ 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 value_dependent
+
template<typename>
class conditional {
};
diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp
index eaa6cb04c5d1c..13846553421ce 100644
--- a/clang/test/SemaCXX/source_location.cpp
+++ b/clang/test/SemaCXX/source_location.cpp
@@ -9,7 +9,9 @@
// RUN: %clang_cc1 -std=c++2b -fcxx-exceptions -DUSE_CONSTEVAL -DPAREN_INIT -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -fms-compatibility -verify %s
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify -fms-compatibility %s
+#ifndef MS
// expected-no-diagnostics
+#endif
#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42)
#define CURRENT_FROM_MACRO() SL::current()
@@ -1088,6 +1090,9 @@ namespace GH178324 {
using e = int;
};
void current(const char * = __builtin_FUNCSIG());
- template <class> void c() { decltype(a(current()))::e; }
+ template <class> void c() {
+ decltype(a(current()))::e;
+ // expected-warning at -1 {{declaration does not declare anything}}
+ }
} // namespace GH178324
#endif
diff --git a/clang/test/SemaCXX/typeof.cpp b/clang/test/SemaCXX/typeof.cpp
index 421cfc59f5311..e6d3dbccf7321 100644
--- a/clang/test/SemaCXX/typeof.cpp
+++ b/clang/test/SemaCXX/typeof.cpp
@@ -8,6 +8,6 @@ namespace GH97646 {
template<bool B>
void f() {
__typeof__(B) x = false;
- !x;
+ !x; // expected-warning {{expression result unused}}
}
}
diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
index 9811b18f4301b..0939a2a7fbb5f 100644
--- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
+++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
@@ -533,12 +533,12 @@ template <class T>
void X<T>::foo() requires requires { requires is_not_same_v<T, int>; } {} // ok
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}}
+void X<T>::bar(decltype(requires { requires something_interesting<T>; })) {} // #GH74314-bar-prev-def-here
template <class T>
void X<T>::bar(decltype(requires { requires is_not_same_v<T, int>; })) {}
+// expected-error at -1 {{redefinition of 'bar'}}
+// expected-note@#GH74314-bar-prev-def-here {{previous definition is here}}
} // namespace GH74314
namespace GH56482 {
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 429df756c1c4f..4e89c814e20b1 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1824,3 +1824,12 @@ 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
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index 9e5756ffec3fc..29693e1918263 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -854,17 +854,17 @@ CC c{};
// CHECK-LABEL: Dumping GH133132::<deduction guide for CC>:
// CHECK-NEXT: FunctionTemplateDecl {{.+}} implicit <deduction guide for CC>
-// CHECK-NEXT: |-NonTypeTemplateParmDecl {{.+}} 'int' depth 0 index 0 N
-// CHECK-NEXT: | `-TemplateArgument {{.+}} expr '42'
-// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 42
-// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} class depth 0 index 1 U
-// CHECK-NEXT: | `-TemplateArgument type 'A<decltype(N)>'
-// CHECK-NEXT: | `-TemplateSpecializationType {{.+}} 'A<decltype(N)>' dependent
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} class depth 0 index 0 U
+// CHECK-NEXT: | `-TemplateArgument type 'A<decltype(N)>':'GH133132::A<int>'
+// CHECK-NEXT: | `-TemplateSpecializationType {{.+}} 'A<decltype(N)>' sugar instantiation_dependent
// CHECK-NEXT: | |-name: 'A':'GH133132::A' qualified
// CHECK-NEXT: | | `-ClassTemplateDecl {{.+}} A
-// CHECK-NEXT: | `-TemplateArgument type 'decltype(N)'
-// CHECK-NEXT: | `-DecltypeType {{.+}} 'decltype(N)' dependent
-// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' NonTypeTemplateParm {{.+}} 'N' 'int'
+// CHECK-NEXT: | |-TemplateArgument type 'decltype(N)':'int'
+// CHECK-NEXT: | | `-DecltypeType {{.+}} 'decltype(N)' sugar instantiation_dependent
+// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'int' NonTypeTemplateParm {{.+}} 'N' 'int'
+// CHECK-NEXT: | | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT: | `-RecordType {{.+}} 'GH133132::A<int>' canonical
+// CHECK-NEXT: | `-ClassTemplateSpecialization {{.+}} 'A'
// CHECK-NEXT: |-TypeTraitExpr {{.+}} 'bool' __is_deducible
// CHECK-NEXT: | |-DeducedTemplateSpecializationType {{.+}} 'GH133132::CC' dependent
// CHECK-NEXT: | | `-name: 'GH133132::CC'
@@ -872,14 +872,13 @@ CC c{};
// CHECK-NEXT: | `-TemplateSpecializationType {{.+}} 'GH133132::A<U>' dependent
// CHECK-NEXT: | |-name: 'GH133132::A'
// CHECK-NEXT: | | `-ClassTemplateDecl {{.+}} A
-// CHECK-NEXT: | `-TemplateArgument type 'U':'type-parameter-0-1'
+// CHECK-NEXT: | `-TemplateArgument type 'U':'type-parameter-0-0'
// CHECK-NEXT: | `-SubstTemplateTypeParmType {{.+}} 'U' sugar dependent class depth 0 index 0 _Ty
// CHECK-NEXT: | |-FunctionTemplate {{.+}} '<deduction guide for A>'
-// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
+// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 0
// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'U'
// CHECK-NEXT: |-CXXDeductionGuideDecl {{.+}} implicit <deduction guide for CC> 'auto () -> GH133132::A<U>'
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} implicit used <deduction guide for CC> 'auto () -> GH133132::A<GH133132::A<int>>' implicit_instantiation
-// CHECK-NEXT: |-TemplateArgument integral '42'
// CHECK-NEXT: `-TemplateArgument type 'GH133132::A<int>'
// CHECK-NEXT: `-RecordType {{.+}} 'GH133132::A<int>'
// CHECK-NEXT: `-ClassTemplateSpecialization {{.+}} 'A'
diff --git a/clang/test/SemaTemplate/dependent-expr.cpp b/clang/test/SemaTemplate/dependent-expr.cpp
index ce210d9b74f6d..31ee43ab9501c 100644
--- a/clang/test/SemaTemplate/dependent-expr.cpp
+++ b/clang/test/SemaTemplate/dependent-expr.cpp
@@ -13,12 +13,12 @@ namespace PR6045 {
static const unsigned int member = r;
void f();
};
-
+
template<unsigned int r>
const unsigned int A<r>::member;
-
+
template<unsigned int r>
- void A<r>::f()
+ void A<r>::f()
{
unsigned k;
(void)(k % member);
@@ -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/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/instantiation-dependence.cpp b/clang/test/SemaTemplate/instantiation-dependence.cpp
index 1a97dbf769ec6..df2acd4525e93 100644
--- a/clang/test/SemaTemplate/instantiation-dependence.cpp
+++ b/clang/test/SemaTemplate/instantiation-dependence.cpp
@@ -1,4 +1,4 @@
-// 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...
@@ -48,17 +48,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 +64,9 @@ namespace PR46791 { // also PR45782
struct D : B, C {};
static_assert(trait<A>::specialization == 0);
- static_assert(trait<B>::specialization == 1); // FIXME expected-error {{failed}} \
- // expected-note {{evaluates to '0 == 1'}}
- static_assert(trait<C>::specialization == 2); // FIXME expected-error {{failed}} \
- // expected-note {{evaluates to '0 == 2'}}
- static_assert(trait<D>::specialization == 0); // FIXME-error {{ambiguous partial specialization}}
+ static_assert(trait<B>::specialization == 1);
+ static_assert(trait<C>::specialization == 2);
+ static_assert(trait<D>::specialization == 0); // expected-error {{ambiguous partial specialization}}
}
namespace TypeQualifier {
@@ -103,3 +97,75 @@ 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<typename T, typename = void> struct X;
+ template<typename T> struct X<T, void_t<char>> {}; // expected-note {{previous}}
+ template<typename T> struct X<T, void_t<void>> {}; // expected-error {{redefinition}}
+} // namespace NonInstDependentArgs1
+
+namespace NonInstDependentArgs2 {
+ template<typename T, typename = void> struct X;
+ template<typename T> struct X<T, void_t<T, void>> {};
+ template<typename T> struct X<T, void_t<T, char>> {};
+} // namespace NonInstDependentArgs2
+
+namespace Level1 {
+ template<typename T, typename = void> struct X;
+ template<typename T> struct X<T, void_t<T>> {};
+ template<typename T> struct X<T, void_t<T*>> {};
+} // namespace Level1
+
+namespace Level2 {
+ template<typename T, typename = void> struct X;
+ template<typename T> struct X<T, void_t<void_t<T>>> {};
+ template<typename T> struct X<T, void_t<void_t<T*>>> {};
+} // namespace Level2
+
+namespace IndirectAlias1 {
+ template<class T> using alias = void_t<T>;
+ template<typename T, typename = void> struct X;
+ template<typename T> struct X<T, void_t<T>> {}; // expected-note {{previous}}
+ template<typename T> struct X<T, alias<T>> {}; // expected-error {{redefinition}}
+} // namspace IndirectAlias1
+
+namespace IndirectAlias2 {
+ template<class T, class> using alias1 = T;
+ template<class T, class U> using alias2 = alias1<T, U>;
+ template<typename T, typename = void> struct X;
+ template<typename T> struct X<T, T> {};
+ template<typename T> struct X<T, alias1<T, T>> {}; // expected-note {{previous}}
+ template<typename T> struct X<T, alias2<T, T>> {}; // expected-error {{redefinition}}
+} // namespaceIndirectAlias2
+
+namespace PackIndexing {
+ // FIXME: This should not be a redefinition.
+ template<class ...Ts> using alias = Ts...[0];
+ template<typename T, typename = void> struct X;
+ template<typename T> struct X<T, T> {}; // expected-note {{previous}}
+ template<typename T> struct X<T, alias<T, typename T::type>> {}; // expected-error {{redefinition}}
+} // namespace PackIndexing
+
+namespace DeclType {
+ template<typename T, typename = void> struct X;
+ template<typename T> struct X<T, decltype(void())> {};
+ template<typename T> struct X<T, decltype(void_t<typename T::type>())> {}; // FIXME-note {{previous}}
+ template<typename T> struct X<T, decltype(void_t<typename T::type>())> {}; // FIXME-error {{redefinition}}
+} // namespace DeclType
+
+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
diff --git a/clang/test/SemaTemplate/partial-spec-instantiate.cpp b/clang/test/SemaTemplate/partial-spec-instantiate.cpp
index 44b58008a1d33..3936bc9ac6c41 100644
--- a/clang/test/SemaTemplate/partial-spec-instantiate.cpp
+++ b/clang/test/SemaTemplate/partial-spec-instantiate.cpp
@@ -165,3 +165,10 @@ namespace GH162855 {
template struct C<D<int>>;
} // namespace GH162855
#endif
+
+namespace dependent_with_qualifier {
+ template <class> struct A;
+ template <class> struct B;
+ template <class T> struct A<const B<T> > {};
+ template struct A<const B<int> >;
+} // namespace dependent_with_qualifier
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 {
More information about the cfe-commits
mailing list