Issue 128364
Summary [Clang++] What type alias should be selected when used together with "deducing this"?
Labels clang
Assignees
Reporter BlankSpruce
    I have an inheritance chain where derived class does something extra before delegating to base class implementation. 

<details>
<summary>Here's an example implementing this kind of behaviour with virtual functions</summary>

```c++
#include <print>

struct Solver
{
    int state = 0;

    int step()
    {
        if (state++ == 2)
        {
 return state;
        }
        return solve();
    }

 virtual int solve()
    {
        return step();
    }
};

struct VerboseSolver : public Solver
{
    using Base = Solver;

    int solve() override
    {
        std::println("Current state: {}", state);
 return Base::solve();
    }
};

struct ExtraVerboseSolver : public VerboseSolver
{
    using Base = VerboseSolver;

    int solve() override
    {
        std::println("Remaining iterations (prediction): {}", 3 - state);
        return Base::solve();
    }
};

template <typename SolverT>
void example()
{
    SolverT solver{};

    auto result = solver.solve();
    std::println("Result: {}", result);
 std::println("");
}

int main()
{
    std::println("Example: Solver");
    example<Solver>();

    std::println("Example: VerboseSolver");
    example<VerboseSolver>();

 std::println("Example: ExtraVerboseSolver");
 example<ExtraVerboseSolver>();
}
```
</details>

<details>
<summary>Expected outcome:</summary>

```
Example: Solver
Result: 3

Example: VerboseSolver
Current state: 0
Current state: 1
Current state: 2
Result: 3

Example: ExtraVerboseSolver
Remaining iterations (prediction): 3
Current state: 0
Remaining iterations (prediction): 2
Current state: 1
Remaining iterations (prediction): 1
Current state: 2
Result: 3
```
</details>

[Example on Godbolt.](https://godbolt.org/z/nsh9d6dT9) All three major compilers agree on the visible behaviour.

I've tried expressing this behaviour with "deducing this" feature to avoid virtual functions and maintain cooperative nature of all these classes. 

<details>
<summary>Code:</summary>

```c++
#include <print>

struct Solver
{
    int state = 0;

    template <typename Self>
    int step(this Self&& self)
    {
        if (self.state++ == 2)
        {
 return self.state;
        }
        return self.solve();
 }

    template <typename Self>
    int solve(this Self&& self)
    {
 return self.step();
    }
};

struct VerboseSolver : public Solver
{
    using Base = Solver;

    template <typename Self>
    int solve(this Self&& self)
    {
        std::println("Current state: {}", self.state);
        return self.Base::solve();
    }
};

struct ExtraVerboseSolver : public VerboseSolver
{
    using Base = VerboseSolver;

    template <typename Self>
    int solve(this Self&& self)
    {
        std::println("Remaining iterations (prediction): {}", 3 - self.state);
        return self.Base::solve();
    }
};

template <typename SolverT>
void example()
{
    SolverT solver{};

    auto result = solver.solve();
    std::println("Result: {}", result);
 std::println("");
}

int main()
{
    std::println("Example: Solver");
    example<Solver>();

    std::println("Example: VerboseSolver");
    example<VerboseSolver>();

 std::println("Example: ExtraVerboseSolver");
 example<ExtraVerboseSolver>();
}
```
</details>

The issue arises when I want to access base class function by referring through type alias for that base class (`self.Base::solve()`). In `ExtraVerboseSolver` example it seems that `Base` type alias visible in `VerboseSolver::solve` is one defined in `ExtraVerboseSolver` which leads to infinite recursion `VerboseSolver::solve -> VerboseSolver::solve`. As far as I can guess it looks like that `Base` alias in `VerboseSolver` is shadowed by `Base` alias in `ExtraVerboseSolver` as if "deducing this" contributed to type alias resolution as well. It can be mitigated by using base class name instead of a type alias. However GCC doesn't require that mitigation and behaviour follows my virtual functions example. Example on Godbolt for all three major compilers with and without type alias: https://godbolt.org/z/4fYK8s344

Unfortunately I don't know what's standard compliant behaviour hence the question in the title. Is GCC right in this case?

_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to