[clang] [clang] Share normalized constraints between decls with same constraint expressions (PR #226620)

Nico Weber via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 27 22:06:36 PDT 2026


================
@@ -15186,6 +15186,11 @@ class Sema final : public SemaBase {
   /// here.
   llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint *>
       NormalizationCache;
+  /// Caches the normal form of constraint expressions (and their pack
+  /// substitution index). These are shared by e.g. the members of all
+  /// specializations of a class template. Used to fill NormalizationCache.
+  llvm::DenseMap<std::pair<const Expr *, unsigned>, NormalizedConstraint *>
+      NormalizedConstraintExprCache;
----------------
nico wrote:

If I understand you right, you're suggesting to use `llvm::DenseMap<const NamedDecl *, NormalizedConstraint *> NormalizationCache;` here instead of `NormalizedConstraintExprCache`, yes? (If not, ignore the following.)

I tried this, and I think it doesn't work. Two different cases:

1. Like clang/test/SemaCXX/fold_lambda_with_variadics.cpp:

```
template <class T, class U> concept Same = __is_same(T, U);
template <class... Ts> void g() {
  ([](Same<Ts> auto x) {}(Ts()), ...);
}
template void g<int, long>();
```

Here, -ast-dump shows the same ConceptSpecializationExpr 0x13f152308 for both instantiated lambdas. Both lambdas refert to the same pattern operator(). So if the cache doesn't contain pack substitution index, it will get this case wrong.

2. Like clang/test/SemaTemplate/concepts-lambda.cpp:

```
template <auto F> concept Callable = requires { F.template operator()<int>(); };
template <auto Pred>
concept P = Callable<[]<class X>
                       requires __is_same(decltype(Pred.template operator()<X>()), bool)
                     {}>;
template <auto Pred> requires P<Pred> constexpr int v = 0;
constexpr auto L1 = []<class T> { return true; };
constexpr auto L2 = []<class T> { return 1; };
int a = v<L1>;
int b = v<L2>;  // should be rejected
```

-ast-dump doesn't show constraint-only lambdas, but debug prints show two expressions referring to a single template. So if we use the template as key, the L2 check reuses the normal form built for L1, and v<L2> gets accepted.



https://github.com/llvm/llvm-project/pull/226620


More information about the cfe-commits mailing list