Skip to content

[Clang] [Modules] Importing a function returning a transform view containing a lambda causes problems. #116087

@Sirraide

Description

@Sirraide

Consider (https://godbolt.org/z/97rdzMq58):

// a.ccm
module; 
#include <ranges>
#include <vector>
export module A;

export std::vector<int> vec;
export auto bar() {
    return vec | std::views::transform([](auto& s) -> int& { return s; });
}

// b.ccm
module;
#include <ranges>
#include <vector>
export module B;
import A;

// Identical to 'bar()' above.
auto bar2() {
    return vec | std::views::transform([](auto& s) -> int& { return s; });
}

void foo() {
    bar2() | std::views::transform([](auto s) { return s; }); // Ok.
    bar()  | std::views::transform([](auto s) { return s; }); // Error? 
}

In foo() in module B, we call bar(), which is imported from module A and returns a transform view that is passed a lambda. Attempting to pipe that to another transform view fails, even though it works just fine if we define the function in the same module as foo

/opt/compiler-explorer/clang-assertions-trunk/bin/clang++ --gcc-toolchain=/opt/compiler-explorer/gcc-snapshot   -fcolor-diagnostics -fno-crash-diagnostics -std=c++26 -isystem/opt/compiler-explorer/libs/fmt/7.1.3/include -O2 -g -DNDEBUG -std=c++26 -MD -MT CMakeFiles/bug.dir/b.ccm.o -MF CMakeFiles/bug.dir/b.ccm.o.d @CMakeFiles/bug.dir/b.ccm.o.modmap -o CMakeFiles/bug.dir/b.ccm.o -c /app/b.ccm
In module 'A' imported from /app/b.ccm:5:
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:224:2: error: no matching constructor for initialization of '(lambda at /app/a.ccm:8:40)'
  224 |         __box(__box&&) = default;
      |         ^~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:1871:11: note: in defaulted move constructor for 'std::ranges::__detail::__box<(lambda at /app/a.ccm:8:40)>' first required here
 1871 |     class transform_view : public view_interface<transform_view<_Vp, _Fp>>
      |           ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:1431:13: note: in implicit move constructor for 'std::ranges::transform_view<std::ranges::ref_view<std::vector<int>>, (lambda at /app/a.ccm:8:40)>' first required here
 1431 |             return std::forward<_Range>(__r);
      |                    ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:1444:30: note: in instantiation of function template specialization 'std::ranges::views::_All::operator()<std::ranges::transform_view<std::ranges::ref_view<std::vector<int>>, (lambda at /app/a.ccm:8:40)>>' requested here
 1444 |       using all_t = decltype(all(std::declval<_Range>()));
      |                              ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:2221:60: note: in instantiation of template type alias 'all_t' requested here
 2221 |     transform_view(_Range&&, _Fp) -> transform_view<views::all_t<_Range>, _Fp>;
      |                                                            ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:2229:17: note: while substituting deduced template arguments into function template '<deduction guide for transform_view>' [with _Range = std::ranges::transform_view<std::ranges::ref_view<std::vector<int>>, (lambda at /app/a.ccm:8:40)>, _Fp = (lambda at /app/b.ccm:17:35)]
 2229 |           = requires { transform_view(std::declval<_Range>(), std::declval<_Fp>()); };
      |                        ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:2229:17: note: (skipping 13 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:931:9: note: while substituting template arguments into constraint expression here
  931 |       = requires { std::declval<_Adaptor>()(declval<_Args>()...); };
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:969:10: note: while checking the satisfaction of concept '__adaptor_invocable<std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, (lambda at /app/b.ccm:17:35)>, std::ranges::transform_view<std::ranges::ref_view<std::vector<int>>, (lambda at /app/a.ccm:8:40)>>' requested here
  969 |       && __adaptor_invocable<_Self, _Range>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ranges:969:10: note: while substituting template arguments into constraint expression here
  969 |       && __adaptor_invocable<_Self, _Range>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
b.ccm:17:11: note: while checking constraint satisfaction for template 'operator|<std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, (lambda at /app/b.ccm:17:35)>, std::ranges::transform_view<std::ranges::ref_view<std::vector<int>>, (lambda at /app/a.ccm:8:40)>>' required here
   17 |     bar() | std::views::transform([](auto s) { return s; });
      |           ^
b.ccm:17:11: note: in instantiation of function template specialization 'std::ranges::views::__adaptor::operator|<std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, (lambda at /app/b.ccm:17:35)>, std::ranges::transform_view<std::ranges::ref_view<std::vector<int>>, (lambda at /app/a.ccm:8:40)>>' requested here
a.ccm:8:40: note: candidate template ignored: could not match 'auto (*)(auto &) -> int &' against '(lambda at /app/a.ccm:8:40)'
    8 |     return vec | std::views::transform([](auto& s) -> int& { return s; });
      |                                        ^

We seem to be failing to... construct a lambda that has no captures, which is weird. Considering that the problem also goes away if I pass e.g. std::identity{} to transform in bar(), this feels like it’s just #110401 all over again, but this time we’re failing to find the right constructor.

I might look into that later today or tomorrow if I can find the time.

CC @ChuanqiXu9

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:frontendLanguage frontend issues, e.g. anything involving "Sema"clang:modulesC++20 modules and Clang Header Modulesneeds-reductionLarge reproducer that should be reduced into a simpler formrejects-valid

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions