llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.93k stars 11.53k forks source link

Fully resolve a concept's arguments in error messages. #53326

Open tomboehmer opened 2 years ago

tomboehmer commented 2 years ago

Problem

The following code generates an error message that is not expressive enough.

#include <utility>

struct S
{
    void operator()(S&&);
};

template<typename F, typename... Args>
concept CallableWith = requires
{
    std::declval<F>()(std::declval<Args>()...);
};

// These might be an implementation detail of a third party library.
template<typename T> using fancy_type_transformation_t = S&;
template<typename T> using strange_type_transformation_t = S&;

int main()
{
    // Expected: Error message should say what {fancy,strange}_type_transformation_t resolved
    //           to.
    // Issue:    It does not.
    static_assert(
        CallableWith<
            strange_type_transformation_t<int>,
            fancy_type_transformation_t<int>>);
}

This is the error message:

resolve_arg.main.cxx:26:2: error: static_assert failed
        static_assert(
        ^
resolve_arg.main.cxx:27:3: note: because 'CallableWith<strange_type_transformation_t<int>, fancy_type_transformation_t<int> >' evaluated to false
                CallableWith<
                ^
resolve_arg.main.cxx:11:2: note: because 'std::declval<F>()(std::declval<Args>()...)' would be invalid: no matching function for call to object of type 'S'
        std::declval<F>()(std::declval<Args>()...);
        ^
1 error generated.

In particular the line

resolve_arg.main.cxx:27:3: note: because 'CallableWith<strange_type_transformation_t<int>, fancy_type_transformation_t<int> >' evaluated to false

does not contain any information about what is supposed to be called and with what. So it is practically useless.

Desired behaviour

Somewhere along the line print something like

resolve_arg.main.cxx:27:3: note: because 'CallableWith<S&, S&>' evaluated to false
cor3ntin commented 2 months ago

@mizvekov I think the ask here is to display both the sugared and desugared name. WDYT?

mizvekov commented 2 months ago

Yeah, sure when displaying a type, we do also display the canonical type when that differs, assuming that would be helpful.

But this is asking for something completely different, we have a boolean expression for the static_assert, and we have never considered printing a desugared expression as far as I know.

That's feasible, but might require building considerable infrastructure.

We could limit the scope of the problem, by for example only handling boolean expressions composed from concept specializations, but that does not seem specially motivating.

Also consider that desugaring might not always be helpful.

In some circumstances, the sugar is not understandable to the user, and the desugared thing is. In other circumstances, the opposite might be true. Consider an ugly named class hidden in some detail namespace, which the user is only supposed to access through a typedef. In yet other circumstances, a more complex thing can be composed of a mix of both things above, so that it doesn't 'look good' when printed either way.