Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][Sema] Populate function template depth at AddTemplateOverloadCandidate #80395

Merged
merged 3 commits into from
Feb 3, 2024

Conversation

zyn0217
Copy link
Contributor

@zyn0217 zyn0217 commented Feb 2, 2024

This is yet another one-line patch to fix crashes on constraint substitution.

template <class, class> struct formatter;

template <class, class> struct basic_format_context {};

template <typename CharType>
concept has_format_function = format(basic_format_context<CharType, CharType>());

template <typename ValueType, typename CharType>
  requires has_format_function<CharType>
struct formatter<ValueType, CharType> {
  template <typename OutputIt>
  CharType format(basic_format_context<OutputIt, CharType>);
};

In this case, we would build up a RecoveryExpr for a call within a constraint expression due to the absence of viable functions. The heuristic algorithm attempted to find such a function inside of a ClassTemplatePartialSpecialization, from which we started to substitute its requires-expression, and it succeeded with a FunctionTemplate such that

  1. It has only one parameter, which is dependent.
  2. The only one parameter depends on two template parameters. They are, in canonical form, <template-parameter-1-0> and <template-parameter-0-1> respectively.

Before we emit an error, we still want to recover the most viable functions. This goes downhill to deducing template parameters against its arguments, where we would collect the argument type with the same depth as the parameter type into a Deduced set. The size of the set is presumed to be that of function template parameters, which is 1 in this case. However, since we haven't yet properly set the template depth before the dance, we'll end up putting the type for <template-parameter-0-1> to the second position of Deduced set, which is unfortunately an access violation!

The bug seems to appear since clang 12.0.

This fixes the case.

@zyn0217 zyn0217 changed the title [clang][Sema] GH58548 [clang][Sema] Populate function template depth at AddTemplateOverloadCandidate. Feb 2, 2024
@zyn0217 zyn0217 changed the title [clang][Sema] Populate function template depth at AddTemplateOverloadCandidate. [clang][Sema] Populate function template depth at AddTemplateOverloadCandidate Feb 2, 2024
@zyn0217 zyn0217 added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label Feb 2, 2024
@zyn0217 zyn0217 marked this pull request as ready for review February 2, 2024 09:29
@llvmbot llvmbot added the clang Clang issues not falling into any other category label Feb 2, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 2, 2024

@llvm/pr-subscribers-clang

Author: Younan Zhang (zyn0217)

Changes

This is yet another one-line patch to fix crashes on constraint substitution.

template &lt;class, class&gt; struct formatter;

template &lt;class, class&gt; struct basic_format_context {};

template &lt;typename CharType&gt;
concept has_format_function = format(basic_format_context&lt;CharType, CharType&gt;());

template &lt;typename ValueType, typename CharType&gt;
  requires has_format_function&lt;CharType&gt;
struct formatter&lt;ValueType, CharType&gt; {
  template &lt;typename OutputIt&gt;
  CharType format(basic_format_context&lt;OutputIt, CharType&gt;);
};

In this case, we would build up a RecoveryExpr for a call within a constraint expression due to the absence of viable functions. The heuristic algorithm attempted to find such a function inside of a ClassTemplatePartialSpecialization, from which we started to substitute its requires-expression, and it succeeded with a FunctionTemplate such that

  1. It has only one parameter, which is dependent.
  2. The only one parameter depends on two template parameters. They are, in canonical form, &lt;template-parameter-1-0&gt; and &lt;template-parameter-0-1&gt; respectively.

Before we emit an error, we still want to recover the most viable functions. This goes downhill to deducing template parameters against its arguments, where we would collect the argument type with the same depth as the parameter type into a Deduced set. The size of the set is presumed to be that of function template parameters, which is 1 in this case. However, since we haven't yet properly set the template depth before the dance, we'll end up putting the type for &lt;template-parameter-0-1&gt; to the second position of Deduced set, which is unfortunately an access violation!

The bug seems to appear since clang 12.0.

This should fix #58548.


Full diff: https://github.com/llvm/llvm-project/pull/80395.diff

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Sema/SemaOverload.cpp (+2-1)
  • (modified) clang/test/SemaTemplate/concepts-recovery-expr.cpp (+27)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 53040aa0f9074..028afba2de6cd 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -186,6 +186,9 @@ Bug Fixes to C++ Support
 - Fix for crash when using a erroneous type in a return statement.
   Fixes (`#63244 <https://github.com/llvm/llvm-project/issues/63244>`_)
   and (`#79745 <https://github.com/llvm/llvm-project/issues/79745>`_)
+- Fixed an out-of-bounds error caused by building a recovery expression for ill-formed
+  function calls while substituting into constraints.
+  (`#58548 <https://github.com/llvm/llvm-project/issues/58548>`_)
 - Fix incorrect code generation caused by the object argument of ``static operator()`` and ``static operator[]`` calls not being evaluated.
   Fixes (`#67976 <https://github.com/llvm/llvm-project/issues/67976>`_)
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 940bcccb9e261..6a04d68b4f041 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7623,7 +7623,8 @@ void Sema::AddTemplateOverloadCandidate(
   //   functions. In such a case, the candidate functions generated from each
   //   function template are combined with the set of non-template candidate
   //   functions.
-  TemplateDeductionInfo Info(CandidateSet.getLocation());
+  TemplateDeductionInfo Info(CandidateSet.getLocation(),
+                             FunctionTemplate->getTemplateDepth());
   FunctionDecl *Specialization = nullptr;
   ConversionSequenceList Conversions;
   if (TemplateDeductionResult Result = DeduceTemplateArguments(
diff --git a/clang/test/SemaTemplate/concepts-recovery-expr.cpp b/clang/test/SemaTemplate/concepts-recovery-expr.cpp
index 2f9d432ebac0e..b338f3bc271bf 100644
--- a/clang/test/SemaTemplate/concepts-recovery-expr.cpp
+++ b/clang/test/SemaTemplate/concepts-recovery-expr.cpp
@@ -180,3 +180,30 @@ void StaticMemOVCUse() {
   // expected-note@#SMEMOVC3 {{candidate template ignored: constraints not satisfied}}
   // expected-note@#SMEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 }
+
+namespace GH58548 {
+
+template <class, class> struct formatter; // #primary-template
+template <class, class> struct basic_format_context {};
+
+template <typename CharType>
+concept has_format_function =
+    format(basic_format_context<CharType, CharType>());
+
+template <typename ValueType, typename CharType>
+  requires has_format_function<CharType>
+struct formatter<ValueType, CharType> {
+  template <typename OutputIt>
+  CharType format(basic_format_context<OutputIt, CharType>);
+};
+
+template <class Ctx> int handle_replacement_field(Ctx arg) {
+  formatter<decltype(arg), int> ctx; // expected-error {{implicit instantiation of undefined template}}
+  return 0;
+}
+
+int x = handle_replacement_field(0);
+// expected-note@-1 {{template specialization 'GH58548::handle_replacement_field<int>' requested here}}
+// expected-note@#primary-template {{is declared here}}
+
+} // GH58548

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, Thanks!

@cor3ntin
Copy link
Contributor

cor3ntin commented Feb 2, 2024

if that's impacting std::format we might want to backport @AaronBallman @erichkeane

@zyn0217
Copy link
Contributor Author

zyn0217 commented Feb 2, 2024

Thanks for the quick review!
I tested the original bug report without the patch but couldn't reproduce it. Nevertheless, the example produced by @royjacobson (which still crashes the trunk) was fixed by this patch.
I'm not sure if this merits a backport?

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see much value in a backport, particularly since this affects back to Clang 12. Else, LGTM.

@zyn0217 zyn0217 merged commit 141de74 into llvm:main Feb 3, 2024
9 checks passed
agozillon pushed a commit to agozillon/llvm-project that referenced this pull request Feb 5, 2024
…Candidate (llvm#80395)

This is yet another one-line patch to fix crashes on constraint
substitution.

```cpp
template <class, class> struct formatter;

template <class, class> struct basic_format_context {};

template <typename CharType>
concept has_format_function = format(basic_format_context<CharType, CharType>());

template <typename ValueType, typename CharType>
  requires has_format_function<CharType>
struct formatter<ValueType, CharType> {
  template <typename OutputIt>
  CharType format(basic_format_context<OutputIt, CharType>);
};
```

In this case, we would build up a `RecoveryExpr` for a call within a
constraint expression due to the absence of viable functions. The
heuristic algorithm attempted to find such a function inside of a
ClassTemplatePartialSpecialization, from which we started to substitute
its requires-expression, and it succeeded with a FunctionTemplate such
that

1) It has only one parameter, which is dependent.
2) The only one parameter depends on two template parameters. They are,
in canonical form, `<template-parameter-1-0>` and
`<template-parameter-0-1>` respectively.

Before we emit an error, we still want to recover the most viable
functions. This goes downhill to deducing template parameters against
its arguments, where we would collect the argument type with the same
depth as the parameter type into a Deduced set. The size of the set is
presumed to be that of function template parameters, which is 1 in this
case. However, since we haven't yet properly set the template depth
before the dance, we'll end up putting the type for
`<template-parameter-0-1>` to the second position of Deduced set, which
is unfortunately an access violation!

The bug seems to appear since clang 12.0.

This fixes [the
case](llvm#58548 (comment)).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CTAD on constrained templates crashes in an std::format usage
4 participants