[clang] [AST] Make TypeOfExprType's dependence match its actual dependence (PR #223015)
Akira Hatanaka via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 11 11:50:27 PDT 2026
https://github.com/ahatanak created https://github.com/llvm/llvm-project/pull/223015
TypeOfExprType based its dependence bits on the full Expr::getDependence() of its operand, so TypeDependence::Dependent was set even when the operand was merely value-dependent, not type-dependent. For example, the type __typeof(sizeof(T)) was marked dependent even though the operand, sizeof(T), is merely value-dependent, not type-dependent, and its type is size_t regardless of T.
Added a parameter to Dependence::type() and toTypeDependence that indicates whether value dependence should imply type dependence, and use it to compute TypeOfExprType's dependence from only its operand's type dependence.
This follows the review discussion on PR #219324:
https://github.com/llvm/llvm-project/pull/219324#pullrequestreview-5095251894
rdar://184775733
>From 0600bb291d947254fd533907e262f7c57bf0aa28 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Fri, 11 Sep 2026 10:48:21 -0700
Subject: [PATCH] [AST] Make TypeOfExprType's dependence match its actual
dependence
TypeOfExprType based its dependence bits on the full
Expr::getDependence() of its operand, so TypeDependence::Dependent
was set even when the operand was merely value-dependent, not
type-dependent. For example, the type __typeof(sizeof(T)) was marked
dependent even though the operand, sizeof(T), is merely
value-dependent, not type-dependent, and its type is size_t
regardless of T.
Added a parameter to Dependence::type() and toTypeDependence that
indicates whether value dependence should imply type dependence, and
use it to compute TypeOfExprType's dependence from only its
operand's type dependence.
This follows the review discussion on PR #219324:
https://github.com/llvm/llvm-project/pull/219324#pullrequestreview-5095251894
rdar://184775733
---
clang/include/clang/AST/DependenceFlags.h | 18 +++++++++++----
clang/lib/AST/Type.cpp | 6 ++++-
clang/test/SemaCXX/typeof.cpp | 2 +-
.../SemaTemplate/current-instantiation.cpp | 23 +++++++++++++++++++
4 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/AST/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h
index 968fe8dd8c22b..d557b82a12b87 100644
--- a/clang/include/clang/AST/DependenceFlags.h
+++ b/clang/include/clang/AST/DependenceFlags.h
@@ -191,10 +191,18 @@ class Dependence {
return Result;
}
- TypeDependence type() const {
+ // If ValueDependenceImpliesTypeDependence is false, an expression that is
+ // only value-dependent (not type-dependent) does not set
+ // TypeDependence::Dependent on the result. This is appropriate for types
+ // that sugar an expression's type verbatim (e.g., __typeof), where the
+ // type cannot actually vary across instantiations unless the expression's
+ // own type does.
+ TypeDependence type(bool ValueDependenceImpliesTypeDependence = true) const {
+ Bits DependentBits =
+ ValueDependenceImpliesTypeDependence ? Dependent : Dependent & ~Value;
return translate(V, UnexpandedPack, TypeDependence::UnexpandedPack) |
translate(V, Instantiation, TypeDependence::Instantiation) |
- translate(V, Dependent, TypeDependence::Dependent) |
+ translate(V, DependentBits, TypeDependence::Dependent) |
translate(V, Error, TypeDependence::Error) |
translate(V, VariablyModified, TypeDependence::VariablyModified);
}
@@ -275,8 +283,10 @@ inline ExprDependence turnValueToTypeDependence(ExprDependence D) {
}
// Returned type-dependence will never have VariablyModified set.
-inline TypeDependence toTypeDependence(ExprDependence D) {
- return Dependence(D).type();
+inline TypeDependence
+toTypeDependence(ExprDependence D,
+ bool ValueDependenceImpliesTypeDependence = true) {
+ return Dependence(D).type(ValueDependenceImpliesTypeDependence);
}
inline TypeDependence toTypeDependence(NestedNameSpecifierDependence D) {
return Dependence(D).type();
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 480f6d763fa85..83848a48cc216 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4259,7 +4259,11 @@ TypeOfExprType::TypeOfExprType(const ASTContext &Context, Expr *E,
Kind == TypeOfKind::Unqualified && !Can.isNull()
? Context.getUnqualifiedArrayType(Can).getAtomicUnqualifiedType()
: Can,
- toTypeDependence(E->getDependence()) |
+ // __typeof(expr) is always the type of expr. Mere
+ // value-dependence of the expression doesn't make this type
+ // dependent. Only type-dependence of the expression does.
+ toTypeDependence(E->getDependence(),
+ /*ValueDependenceImpliesTypeDependence=*/false) |
(E->getType()->getDependence() &
TypeDependence::VariablyModified)),
TOExpr(E), Context(Context) {
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/current-instantiation.cpp b/clang/test/SemaTemplate/current-instantiation.cpp
index 9214bbeb973d6..fc1ed2a1c54f8 100644
--- a/clang/test/SemaTemplate/current-instantiation.cpp
+++ b/clang/test/SemaTemplate/current-instantiation.cpp
@@ -247,3 +247,26 @@ namespace RebuildDependentScopeDeclRefExpr {
// FIXME: We should issue a typo-correction here.
template<typename T> N<X<T>::think> X<T>::foo() {} // expected-error {{no member named 'think' in 'RebuildDependentScopeDeclRefExpr::X<T>'}}
}
+
+namespace TypeofValueDependence {
+ struct A { int value() const; };
+ A f(unsigned);
+
+ // 'sizeof(T)' makes 'f(sizeof(T))' value-dependent, but overload
+ // resolution for 'f' only looks at argument types, so the type of
+ // 'f(sizeof(T))' is always 'A' regardless of 'T'. '__typeof(f(sizeof(T)))'
+ // is therefore not type-dependent, so member access on it is checked
+ // immediately rather than deferred to instantiation.
+ template<typename T> struct B {
+ int g() {
+ __typeof(f(sizeof(T))) a = f(0);
+ return a.value();
+ }
+ int h() {
+ __typeof(f(sizeof(T))) a = f(0);
+ return a.nope(); // expected-error {{no member named 'nope' in 'TypeofValueDependence::A'}}
+ }
+ };
+
+ int i() { return B<int>().g() + B<int>().h(); }
+}
More information about the cfe-commits
mailing list