Skip to content

Fix support for function multiple forward arguments - close #279 #293

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

Merged

Conversation

filipsajdak
Copy link
Contributor

@filipsajdak filipsajdak commented Mar 25, 2023

The current implementation correctly handles one forward argument - only then produces the correct requires clause.

When there are more forward arguments, like in the following code:

fun: (forward s1 : std::string, forward s2 : std::string, forward s3 : std::string) = {
    // ...
}

Generates:

requires std::is_same_v<CPP2_TYPEOF(s1), std::string>requires std::is_same_v<CPP2_TYPEOF(s2), std::string>requires std::is_same_v<CPP2_TYPEOF(s3), std::string>

This change corrects this behavior and creates requires clause
that joins multiple clauses with && surrounded by parentheses:

requires (std::is_same_v<CPP2_TYPEOF(s1), std::string> && std::is_same_v<CPP2_TYPEOF(s2), std::string> && std::is_same_v<CPP2_TYPEOF(s3), std::string>)

The change maintains the old behavior of not adding parentheses for only one requires clause - all regression tests pass.

Closes #279

The current implementation correctly handle one forward argument
- it produces correct requires clause.

When there are more forward arguments like in following code:
```cpp
fun: (forward s1 : std::string, forward s2 : std::string, forward s3 : std::string) = {
    // ...
}
```
generates:
```cpp
requires std::is_same_v<CPP2_TYPEOF(s1), std::string>requires std::is_same_v<CPP2_TYPEOF(s2), std::string>requires std::is_same_v<CPP2_TYPEOF(s3), std::string>
```

This change correct this behaviour and creates requires clause
that joins multiple clauses with `&&`:
```cpp
requires (std::is_same_v<CPP2_TYPEOF(s1), std::string> && std::is_same_v<CPP2_TYPEOF(s2), std::string> && std::is_same_v<CPP2_TYPEOF(s3), std::string>)
```
@filipsajdak filipsajdak changed the title Add support for function multiple forward arguments - close #279 Fix support for function multiple forward arguments - close #279 Mar 25, 2023
@hsutter hsutter merged commit e606505 into hsutter:main Mar 26, 2023
@hsutter
Copy link
Owner

hsutter commented Mar 26, 2023

Looks good, thanks!

@filipsajdak filipsajdak deleted the fsajdak-fix-multiple-forward-arguments branch March 26, 2023 07:51
@hsutter
Copy link
Owner

hsutter commented Mar 26, 2023

This has also spurred me to push 3e5a83a to get around to adding requires-clause's to Cpp2 UDTs and functions, which will get merged with any generated requires from forward parameters for a function that has both. Regressions are passing, but it's late at night, so I'd appreciate another set of eyes on the code.

Also: I think the requires-clauses should actually go out on the function declarations too, not only on the definitions as it's been doing all this time now even though that seemed to work. However, my first attempt to emit them also on the declarations generated code that worked on Clang and MSVC, but caused 'ambiguous call' errors on GCC which seemed to be treating the declaration and definition as two functions... for now I've committed just the basic requires-clause support, but I'll take another look at moving them up to function declarations once I look into what was happening with GCC.

@hsutter
Copy link
Owner

hsutter commented Mar 26, 2023

Ah, I found the answer... it was a bug in GCC 10.x that was fixed in GCC 11.0 (March 2021).

So I can easily go and emit the requires-clauses also on the function declarations (just move the branch up a little from line 4734 to line 4646). But that would be at the cost of breaking concrete-type forward parameters on GCC 10... which happens to be my main GCC test compiler. Sigh. I'd probably do it if I could easily update my GCC environment (I'm using Ubuntu 20.04 LTS) but it was a pain the last time I tried and I'm loath to spend time on it.

@hsutter
Copy link
Owner

hsutter commented Mar 26, 2023

Documented in c31320d

@filipsajdak
Copy link
Contributor Author

@hsutter I played a little with the custom requires for functions, and they work even for things like:

fun: <X : type, Y : type, Z : type> (forward x : X, forward s1 : std::string, forward y : Y, forward s2 : std::string, forward z : Z, forward s3 : std::string)
    requires std::is_same_v<X, int>
              && (sizeof(Z) > 2u + 2u || sizeof(X) + sizeof(Y) > (1u + 1u) * 2)
 = {
    std::cout << x << s1 << y << s2 << z << s3 << std::endl;
}

main: () = {
    a : std::string = "a";
    b : std::string = "b";
    fun(42, a, "_string_literal_", b, 24L, std::string("b"));
    b = "";
}

Generates:

#define CPP2_USE_MODULES         Yes

#include "cpp2util.h"

#line 1 "../tests/bug_multiple_forward_arguments.cpp2"
template<typename X, typename Y, typename Z> auto fun(X&& x, auto&& s1,       Y&&      y,    auto&&   s2,              Z&&      z,    auto&&   s3) -> void;

#line 8 "../tests/bug_multiple_forward_arguments.cpp2"
auto main() -> int;

//=== Cpp2 definitions ==========================================================

#line 1 "../tests/bug_multiple_forward_arguments.cpp2"
template<typename X, typename Y, typename Z> auto fun(X&& x, auto&& s1,       Y&&      y,    auto&&   s2,              Z&&      z,    auto&&   s3) -> void
requires (std::is_same_v<X,int> && (cpp2::cmp_greater(sizeof(Z),2u + 2u) || cpp2::cmp_greater(sizeof(X) + sizeof(Y),(1u + 1u) * 2)) && std::is_same_v<CPP2_TYPEOF(s1), std::string> && std::is_same_v<CPP2_TYPEOF(s2), std::string> && std::is_same_v<CPP2_TYPEOF(s3), std::string>)
#line 4 "../tests/bug_multiple_forward_arguments.cpp2"
 {
    std::cout << CPP2_FORWARD(x) << CPP2_FORWARD(s1) << CPP2_FORWARD(y) << CPP2_FORWARD(s2) << CPP2_FORWARD(z) << CPP2_FORWARD(s3) << std::endl;
}

auto main() -> int{
    std::string a {"a"}; 
    std::string b {"b"}; 
    fun(42, std::move(a), "_string_literal_", b, 24L, std::string("b"));
    b = "";
}

Compiles and returns:

../tests/bug_multiple_forward_arguments.cpp2... ok (all Cpp2, passes safety checks)

42a_string_literal_b24b

@filipsajdak
Copy link
Contributor Author

@hsutter Regarding gcc-11 I have try to create a Dockerfile that has Ubuntu 20.04 with gcc-11:

# Use the official Ubuntu 20.04 base image
FROM ubuntu:20.04

# Set environment variables to prevent tzdata from prompting during installation
ENV DEBIAN_FRONTEND=noninteractive

# Update and upgrade the system packages
RUN apt-get update && apt-get upgrade -y

# Install dependencies required for adding the repository and installing gcc-11
RUN apt-get install -y --no-install-recommends \
    software-properties-common \
    build-essential \
    wget \
    ca-certificates \
    gnupg \
    lsb-release

# Add the repository for gcc-11 and install it
RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
    apt-get update && \
    apt-get install -y --no-install-recommends gcc-11 g++-11

# Set gcc-11 and g++-11 as default compilers
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 && \
    update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100

# Clean up cache and temporary files
RUN apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Set the working directory
WORKDIR /app

# Set the default command to run when starting the container
CMD ["/bin/bash"]

Build with:

docker build -t cppfront-ubuntu-20.04-gcc11 .

Then I was able to easily compile cppfront:

docker run --rm -v "$(pwd)":/app -w /app cppfront-ubuntu-20.04-gcc11 g++ -std=c++20 -o cppfront-gcc11 source/cppfront.cpp

And later can run it with:

docker run --rm -v "$(pwd)":/app -w /app cppfront-ubuntu-20.04-gcc11 ./cppfront-gcc11 -h

If you are not willing to use docker then you can install gcc-11 with:

add-apt-repository -y ppa:ubuntu-toolchain-r/test && apt-get update && apt-get install -y --no-install-recommends gcc-11 g++-11

zaucy pushed a commit to zaucy/cppfront that referenced this pull request Dec 5, 2023
… (hsutter#293)

* Add support for function multiple forward arguments

The current implementation correctly handle one forward argument
- it produces correct requires clause.

When there are more forward arguments like in following code:
```cpp
fun: (forward s1 : std::string, forward s2 : std::string, forward s3 : std::string) = {
    // ...
}
```
generates:
```cpp
requires std::is_same_v<CPP2_TYPEOF(s1), std::string>requires std::is_same_v<CPP2_TYPEOF(s2), std::string>requires std::is_same_v<CPP2_TYPEOF(s3), std::string>
```

This change correct this behaviour and creates requires clause
that joins multiple clauses with `&&`:
```cpp
requires (std::is_same_v<CPP2_TYPEOF(s1), std::string> && std::is_same_v<CPP2_TYPEOF(s2), std::string> && std::is_same_v<CPP2_TYPEOF(s3), std::string>)
```

* Add regression test for multiple forward arguments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] multiple forward function arguments produce bad requires clause
2 participants