[clang] [clang] ignore value dependence for decltype (PR #190495)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 4 17:16:05 PDT 2026
https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/190495
The 'decltype' for a value-dependent but non-type-dependent expression should be known, so this patch makes them non-opaque instead in that case.
Fixes #61818
>From 9659f2849eb99f6c753c7e243c6b3422b99ad35e 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] 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.
Fixes #61818
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/AST/DependenceFlags.h | 2 +-
clang/lib/AST/ASTContext.cpp | 9 ++++----
clang/lib/AST/ItaniumMangle.cpp | 5 ++++
clang/lib/AST/Type.cpp | 22 ++++++++++--------
.../test/CXX/temp/temp.decls/temp.mem/p5.cpp | 2 +-
clang/test/Sema/invalid-bitwidth-expr.mm | 1 +
clang/test/SemaCXX/decltype.cpp | 9 +++++++-
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 +++----
.../SemaTemplate/temp_arg_template_p0522.cpp | 5 +++-
15 files changed, 71 insertions(+), 40 deletions(-)
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/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h
index c4395259f0758..2f003af925d79 100644
--- a/clang/include/clang/AST/DependenceFlags.h
+++ b/clang/include/clang/AST/DependenceFlags.h
@@ -194,7 +194,7 @@ class Dependence {
TypeDependence type() const {
return translate(V, UnexpandedPack, TypeDependence::UnexpandedPack) |
translate(V, Instantiation, TypeDependence::Instantiation) |
- translate(V, Dependent, TypeDependence::Dependent) |
+ translate(V, Type, TypeDependence::Dependent) |
translate(V, Error, TypeDependence::Error) |
translate(V, VariablyModified, TypeDependence::VariablyModified);
}
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ee7f823b014b2..36978548ca171 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6691,12 +6691,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());
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index d21faaa87558d..46e8b75633d54 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.
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 78983fd38410d..b8362f25ec5d2 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())
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/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..78bcbbe6b6489 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,13 @@ 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'}}
+ }
+} // 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/temp_arg_template_p0522.cpp b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
index bde811c3bf685..73b2be2c72f11 100644
--- a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
@@ -135,7 +135,10 @@ 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;
}
More information about the cfe-commits
mailing list