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 frontend crash with deduction guide inside class template #88142

Closed
elbeno opened this issue Apr 9, 2024 · 5 comments · Fixed by #91628
Closed

Clang frontend crash with deduction guide inside class template #88142

elbeno opened this issue Apr 9, 2024 · 5 comments · Fixed by #91628
Assignees
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" crash Prefer [crash-on-valid] or [crash-on-invalid]

Comments

@elbeno
Copy link

elbeno commented Apr 9, 2024

This seems to be a regression from 17 to 18/trunk.

template <typename, typename...>
struct X {
  template <typename>
  struct Y {
    template <typename T>
    Y(T) {}
  };

  template <typename T, typename... Vs>
  Y(T, Vs...) -> Y<T>;
};

using T = X<int>::Y<int>;

https://godbolt.org/z/K5d3sWGjP

@github-actions github-actions bot added the clang Clang issues not falling into any other category label Apr 9, 2024
@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" crash Prefer [crash-on-valid] or [crash-on-invalid] and removed clang Clang issues not falling into any other category labels Apr 9, 2024
@llvmbot
Copy link
Member

llvmbot commented Apr 9, 2024

@llvm/issue-subscribers-clang-frontend

Author: Ben Deane (elbeno)

This seems to be a regression from 17 to 18/trunk.
template &lt;typename, typename...&gt;
struct X {
  template &lt;typename&gt;
  struct Y {
    template &lt;typename T&gt;
    Y(T) {}
  };

  template &lt;typename T, typename... Vs&gt;
  Y(T, Vs...) -&gt; Y&lt;T&gt;;
};

using T = X&lt;int&gt;::Y&lt;int&gt;;

https://godbolt.org/z/K5d3sWGjP

@lukevalenty
Copy link

This breaks an Intel open source repo: https://github.com/intel/compile-time-init-build. At least in message.hpp, maybe more.

@elbeno
Copy link
Author

elbeno commented Apr 9, 2024

With assertions:

clang++: /root/llvm-project/clang/lib/Sema/SemaTemplateInstantiate.cpp:2533:
clang::QualType {anonymous}::TemplateInstantiator::TransformTemplateTypeParmType(clang::TypeLocBuilder&, clang::TemplateTypeParmTypeLoc, bool):
Assertion `Arg.getKind() == TemplateArgument::Type && "Template argument kind mismatch"' failed.

@shafik
Copy link
Collaborator

shafik commented Apr 9, 2024

Maybe related: #63823

It does indeed look like a regression introduced between clang-17 and clang-18.

CC @zyn0217 @hokein

@zyn0217 zyn0217 self-assigned this May 9, 2024
zyn0217 added a commit to zyn0217/llvm-project that referenced this issue May 9, 2024
zyn0217 added a commit that referenced this issue May 10, 2024
…class templates (#91628)

This fixes a regression introduced by bee78b8.

When we form a deduction guide for a constructor, basically, we do the
following work:
- Collect template parameters from the constructor's surrounding class
template, if present.
- Collect template parameters from the constructor.
- Splice these template parameters together into a new template
parameter list.
- Turn all the references (e.g. the function parameter list) to the
invented parameter list by applying a `TreeTransform` to the function
type.

In the previous fix, we handled cases of nested class templates by
substituting the "outer" template parameters (i.e. those not declared at
the surrounding class template or the constructor) with the
instantiating template arguments. The approach per se makes sense, but
there was a flaw in the following case:

```cpp
template <typename U, typename... Us> struct X {
  template <typename V> struct Y {
    template <typename T> Y(T) {}
  };

  template <typename T> Y(T) -> Y<T>;
};

X<int>::Y y(42);
```

While we're transforming the parameters for `Y(T)`, we first attempt to
transform all references to `V` and `T`; then, we handle the references
to outer parameters `U` and `Us` using the template arguments from
`X<int>` by transforming the same `ParamDecl`. However, the first step
results in the reference `T` being `<template-param-0-1>` because the
invented `T` is the last of the parameter list of the deduction guide,
and what we're substituting with is a corresponding parameter pack
(which is `Us`, though empty). Hence we're messing up the substitution.

I think we can resolve it by reversing the substitution order, which
means handling outer template parameters first and then the inner
parameters.

There's no release note because this is a regression in 18, and I hope
we can catch up with the last release.

Fixes #88142
llvmbot pushed a commit to llvmbot/llvm-project that referenced this issue May 12, 2024
…class templates (llvm#91628)

This fixes a regression introduced by bee78b8.

When we form a deduction guide for a constructor, basically, we do the
following work:
- Collect template parameters from the constructor's surrounding class
template, if present.
- Collect template parameters from the constructor.
- Splice these template parameters together into a new template
parameter list.
- Turn all the references (e.g. the function parameter list) to the
invented parameter list by applying a `TreeTransform` to the function
type.

In the previous fix, we handled cases of nested class templates by
substituting the "outer" template parameters (i.e. those not declared at
the surrounding class template or the constructor) with the
instantiating template arguments. The approach per se makes sense, but
there was a flaw in the following case:

```cpp
template <typename U, typename... Us> struct X {
  template <typename V> struct Y {
    template <typename T> Y(T) {}
  };

  template <typename T> Y(T) -> Y<T>;
};

X<int>::Y y(42);
```

While we're transforming the parameters for `Y(T)`, we first attempt to
transform all references to `V` and `T`; then, we handle the references
to outer parameters `U` and `Us` using the template arguments from
`X<int>` by transforming the same `ParamDecl`. However, the first step
results in the reference `T` being `<template-param-0-1>` because the
invented `T` is the last of the parameter list of the deduction guide,
and what we're substituting with is a corresponding parameter pack
(which is `Us`, though empty). Hence we're messing up the substitution.

I think we can resolve it by reversing the substitution order, which
means handling outer template parameters first and then the inner
parameters.

There's no release note because this is a regression in 18, and I hope
we can catch up with the last release.

Fixes llvm#88142

(cherry picked from commit 8c852ab)
tstellar pushed a commit to llvmbot/llvm-project that referenced this issue May 14, 2024
…class templates (llvm#91628)

This fixes a regression introduced by bee78b8.

When we form a deduction guide for a constructor, basically, we do the
following work:
- Collect template parameters from the constructor's surrounding class
template, if present.
- Collect template parameters from the constructor.
- Splice these template parameters together into a new template
parameter list.
- Turn all the references (e.g. the function parameter list) to the
invented parameter list by applying a `TreeTransform` to the function
type.

In the previous fix, we handled cases of nested class templates by
substituting the "outer" template parameters (i.e. those not declared at
the surrounding class template or the constructor) with the
instantiating template arguments. The approach per se makes sense, but
there was a flaw in the following case:

```cpp
template <typename U, typename... Us> struct X {
  template <typename V> struct Y {
    template <typename T> Y(T) {}
  };

  template <typename T> Y(T) -> Y<T>;
};

X<int>::Y y(42);
```

While we're transforming the parameters for `Y(T)`, we first attempt to
transform all references to `V` and `T`; then, we handle the references
to outer parameters `U` and `Us` using the template arguments from
`X<int>` by transforming the same `ParamDecl`. However, the first step
results in the reference `T` being `<template-param-0-1>` because the
invented `T` is the last of the parameter list of the deduction guide,
and what we're substituting with is a corresponding parameter pack
(which is `Us`, though empty). Hence we're messing up the substitution.

I think we can resolve it by reversing the substitution order, which
means handling outer template parameters first and then the inner
parameters.

There's no release note because this is a regression in 18, and I hope
we can catch up with the last release.

Fixes llvm#88142

(cherry picked from commit 8c852ab)
@elbeno
Copy link
Author

elbeno commented May 15, 2024

Thanks @zyn0217 ! https://github.com/intel/compile-time-init-build is building with clang-18 now.

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" crash Prefer [crash-on-valid] or [crash-on-invalid]
Projects
None yet
6 participants