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

fix: emit optimizing inspect #529

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
319 changes: 159 additions & 160 deletions include/cpp2util.h

Large diffs are not rendered by default.

13 changes: 4 additions & 9 deletions regression-tests/mixed-type-safety-1.cpp2
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,18 @@ class Shape { public: virtual ~Shape() { } };
class Circle : public Shape { };
class Square : public Shape { };

//--- printing helpers -----------------
//--- printing setup -------------------

print: ( msg: std::string, x: _ ) =
std::cout << msg << x << "\n";

print: ( msg: std::string, b: bool ) =

main: () -> int =
{
bmsg: * const char;
if b { bmsg = "true"; }
else { bmsg = "false"; }
std::cout << msg << bmsg << "\n";
}
std::cout << std::boolalpha;

//--- examples -------------------------

main: () -> int =
{
print( "1.1 is int? ", 1.1 is int );
print( "1 is int? ", 1 is int );

Expand Down
151 changes: 151 additions & 0 deletions regression-tests/pure2-bugfix-for-optimizing-inspect.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
main: () = {
_ = :(x) = {
[[assert Testing: inspect x -> bool {
// Condition is `false_type` for `int`, alternative not instantiated.
is std::string = x.substr(0);
is _ = true;
}]]
}(1);

{
pred := :<T> (y: T) -> std::true_type = {
std::terminate();
return ();
};
[[assert Testing: inspect 1 -> bool {
// Condition is `true_type` for `int`, statement unconditionally executed.
is (pred) = true;
is _ = false;
}]]
}

_ = :(x) = {
pred := :<T> (y: T) -> bool requires std::is_same_v<T, long> = {
std::terminate();
return true;
};
[[assert Testing: inspect x -> bool {
// Condition is non-viable for `int`, alternative not instantiated.
is (pred) = x.substr(0);
is _ = true;
}]]
}(1);

// Exercise all built-in `is` overloads.

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is std::vector = x.substr(0);
is std::array = x.substr(0);
is std::string = x.substr(0);
is std::exception = x.substr(0);
is cpp2::empty = x.substr(0);
is (std::ranges::empty) = x.substr(0);
is ("") = x.substr(0);
is _ = true;
}]]
}(1);
_ = :(x) = {
[[assert Testing: inspect x& -> bool {
is std::exception = x.substr(0);
is _ = true;
}]]
}(1);

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is std::vector = true;
is _ = false;
}]]
}(:std::vector = (1));

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is std::array = true;
is _ = false;
}]]
}(:std::array = (1));

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is int = true;
is _ = false;
}]]
}(1);

_ = :(x) = {
[[assert Testing: inspect x -> bool {
// FIXME
// Using `std::ranges::subrange<std::add_pointer_t<i32>>::view_interface` fails.
// The alternative is elided due to the `is` being ambiguous.
// Like in P2392, the cases of the built-in `is` should be a chain of conditions.
// Using overloads to implement that is tedious and error-prone.
is std::ranges::view_interface<std::ranges::subrange<std::add_pointer_t<i32>>> = true;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a [BUG].
This is not a [SUGGESTION] (but could be taken as one).

I have thought of having the function expression's signature be <T> (x: T)
so that I could use T::view_interface in the line I'm commenting on.
But it doesn't work because it's missing the lowered Cpp1 typename in front of it.

Cppfront already recognizes a template function's template parameters in its function parameter list to omit wrapping a function parameter's type in cpp2::in.
So do you think it'd make sense to extend Cpp2 to not require typename in such type-only contexts?

I'm also wondering how you plan to support such disambiguating typename in Cpp2.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I think about it,
the more I'm convinced that
"yes, this should be a thing!"

This is basically Cpp1's optional typename,
but for Cpp2's type-only contexts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #531 for this.

is _ = false;
}]]
}(:std::ranges::subrange<std::add_pointer_t<i32>> = ());

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is std::exception = true;
is _ = false;
}]]
}(:std::bad_exception = ());

_ = :(x) = {
[[assert Testing: inspect x& -> bool {
is std::exception = true;
is _ = false;
}]]
}(:std::bad_exception = ());

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is cpp2::empty = true;
is _ = false;
}]]
}(:std::optional<i32> = ());

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is (std::ranges::empty) = true;
is _ = false;
}]]
}(:std::vector<i32> = ());

_ = :(x) = {
[[assert Testing: inspect x -> bool {
is (1) = true;
is _ = false;
}]]
}(1);

// Exercise `is` overloads for `std::` types.

{
f := :(x) = {
(pred := :<T> (y: T) -> bool requires std::is_same_v<T, i32> = {
std::terminate();
return true;
})
[[assert Testing: inspect x -> bool {
is (:std::span<i32> = ()) = x.substr(0);
is (pred) = x.substr(0);
is (1) = true;
is _ = false;
}]]

(pred := :<T> (y: T) -> std::true_type = {
std::terminate();
return ();
}) {
[[assert Testing: inspect x -> bool {
is (pred) = true;
is _ = false;
}]]
}
};
f(:std::any = 1);
f(:std::optional = 1);
}
}
12 changes: 6 additions & 6 deletions regression-tests/test-results/mixed-inspect-templates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ struct my_type {};
#line 8 "mixed-inspect-templates.cpp2"
[[nodiscard]] auto fun(auto const& v) -> std::string{
return [&] () -> std::string { auto&& __expr = v;
if (cpp2::is<std::vector>(__expr)) { if constexpr( requires{"std::vector";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("std::vector")),std::string> ) return "std::vector"; else return std::string{}; else return std::string{}; }
else if (cpp2::is<std::array>(__expr)) { if constexpr( requires{"std::array";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("std::array")),std::string> ) return "std::array"; else return std::string{}; else return std::string{}; }
else if (cpp2::is<std::variant>(__expr)) { if constexpr( requires{"std::variant";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("std::variant")),std::string> ) return "std::variant"; else return std::string{}; else return std::string{}; }
else if (cpp2::is<my_type>(__expr)) { if constexpr( requires{"my_type";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("my_type")),std::string> ) return "my_type"; else return std::string{}; else return std::string{}; }
else return "unknown"; }
();
if constexpr (requires { cpp2::is<std::vector>(__expr); }) { if constexpr (!std::is_same_v<decltype(cpp2::is<std::vector>(__expr)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is<std::vector>(__expr)), std::true_type>) { return "std::vector"; } else { if (cpp2::is<std::vector>(__expr)) { return "std::vector"; } } } }
if constexpr (requires { cpp2::is<std::array>(__expr); }) { if constexpr (!std::is_same_v<decltype(cpp2::is<std::array>(__expr)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is<std::array>(__expr)), std::true_type>) { return "std::array"; } else { if (cpp2::is<std::array>(__expr)) { return "std::array"; } } } }
if constexpr (requires { cpp2::is<std::variant>(__expr); }) { if constexpr (!std::is_same_v<decltype(cpp2::is<std::variant>(__expr)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is<std::variant>(__expr)), std::true_type>) { return "std::variant"; } else { if (cpp2::is<std::variant>(__expr)) { return "std::variant"; } } } }
if constexpr (requires { cpp2::is<my_type>(__expr); }) { if constexpr (!std::is_same_v<decltype(cpp2::is<my_type>(__expr)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is<my_type>(__expr)), std::true_type>) { return "my_type"; } else { if (cpp2::is<my_type>(__expr)) { return "my_type"; } } } }
return "unknown";
}();
}

[[nodiscard]] auto fun2(auto const& v) -> std::string{
Expand Down
8 changes: 4 additions & 4 deletions regression-tests/test-results/mixed-inspect-values-2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ constexpr auto empty = [](auto&& x){
auto i {15};

std::cout << [&] () -> std::string { auto&& __expr = i;
if (cpp2::is(__expr, (less_than(10)))) { if constexpr( requires{"i less than 10";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("i less than 10")),std::string> ) return "i less than 10"; else return std::string{}; else return std::string{}; }
else if (cpp2::is(__expr, in(11, 20))) { if constexpr( requires{"i is between 11 and 20";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("i is between 11 and 20")),std::string> ) return "i is between 11 and 20"; else return std::string{}; else return std::string{}; }
else return "i is out of our interest"; }
() << std::endl;
if constexpr (requires { cpp2::is(__expr, (less_than(10))); }) { if constexpr (!std::is_same_v<decltype(cpp2::is(__expr, (less_than(10)))), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is(__expr, (less_than(10)))), std::true_type>) { return "i less than 10"; } else { if (cpp2::is(__expr, (less_than(10)))) { return "i less than 10"; } } } }
if constexpr (requires { cpp2::is(__expr, in(11, 20)); }) { if constexpr (!std::is_same_v<decltype(cpp2::is(__expr, in(11, 20))), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is(__expr, in(11, 20))), std::true_type>) { return "i is between 11 and 20"; } else { if (cpp2::is(__expr, in(11, 20))) { return "i is between 11 and 20"; } } } }
return "i is out of our interest";
}() << std::endl;

if (cpp2::is(i, (less_than(20)))) {
std::cout << "less than 20" << std::endl;
Expand Down
16 changes: 8 additions & 8 deletions regression-tests/test-results/mixed-inspect-values.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ auto test(auto const& x) -> void;
auto test(auto const& x) -> void{
auto forty_two {42};
std::cout << [&] () -> std::string { auto&& __expr = x;
if (cpp2::is(__expr, 0)) { if constexpr( requires{"zero";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("zero")),std::string> ) return "zero"; else return std::string{}; else return std::string{}; }
else if (cpp2::is(__expr, (in(1, 2)))) { if constexpr( requires{"1 or 2";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("1 or 2")),std::string> ) return "1 or 2"; else return std::string{}; else return std::string{}; }
else if (cpp2::is(__expr, in_2_3)) { if constexpr( requires{"3";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("3")),std::string> ) return "3"; else return std::string{}; else return std::string{}; }
else if (cpp2::is(__expr, std::move(forty_two))) { if constexpr( requires{"the answer";} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("the answer")),std::string> ) return "the answer"; else return std::string{}; else return std::string{}; }
else if (cpp2::is<int>(__expr)) { if constexpr( requires{"integer " + cpp2::to_string(x);} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF(("integer " + cpp2::to_string(x))),std::string> ) return "integer " + cpp2::to_string(x); else return std::string{}; else return std::string{}; }
else if (cpp2::is<std::string>(__expr)) { if constexpr( requires{cpp2::as<std::string>(x);} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((cpp2::as<std::string>(x))),std::string> ) return cpp2::as<std::string>(x); else return std::string{}; else return std::string{}; }
else return "(no match)"; }
() << "\n";
if constexpr (requires { cpp2::is(__expr, 0); }) { if constexpr (!std::is_same_v<decltype(cpp2::is(__expr, 0)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is(__expr, 0)), std::true_type>) { return "zero"; } else { if (cpp2::is(__expr, 0)) { return "zero"; } } } }
if constexpr (requires { cpp2::is(__expr, (in(1, 2))); }) { if constexpr (!std::is_same_v<decltype(cpp2::is(__expr, (in(1, 2)))), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is(__expr, (in(1, 2)))), std::true_type>) { return "1 or 2"; } else { if (cpp2::is(__expr, (in(1, 2)))) { return "1 or 2"; } } } }
if constexpr (requires { cpp2::is(__expr, in_2_3); }) { if constexpr (!std::is_same_v<decltype(cpp2::is(__expr, in_2_3)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is(__expr, in_2_3)), std::true_type>) { return "3"; } else { if (cpp2::is(__expr, in_2_3)) { return "3"; } } } }
if constexpr (requires { cpp2::is(__expr, std::move(forty_two)); }) { if constexpr (!std::is_same_v<decltype(cpp2::is(__expr, std::move(forty_two))), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is(__expr, std::move(forty_two))), std::true_type>) { return "the answer"; } else { if (cpp2::is(__expr, std::move(forty_two))) { return "the answer"; } } } }
if constexpr (requires { cpp2::is<int>(__expr); }) { if constexpr (!std::is_same_v<decltype(cpp2::is<int>(__expr)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is<int>(__expr)), std::true_type>) { return "integer " + cpp2::to_string(x); } else { if (cpp2::is<int>(__expr)) { return "integer " + cpp2::to_string(x); } } } }
if constexpr (requires { cpp2::is<std::string>(__expr); }) { if constexpr (!std::is_same_v<decltype(cpp2::is<std::string>(__expr)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is<std::string>(__expr)), std::true_type>) { return cpp2::as<std::string>(x); } else { if (cpp2::is<std::string>(__expr)) { return cpp2::as<std::string>(x); } } } }
return "(no match)";
}() << "\n";
}

Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ auto calc() {
#line 7 "mixed-inspect-with-typeof-of-template-arg-list.cpp2"
[[nodiscard]] auto fun(auto const& v) -> int{
return [&] () -> int { auto&& __expr = v;
if (cpp2::is<int>(__expr)) { if constexpr( requires{calc<1,2>();} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((calc<1,2>())),int> ) return calc<1,2>(); else return int{}; else return int{}; }
else return 0; }
();
if constexpr (requires { cpp2::is<int>(__expr); }) { if constexpr (!std::is_same_v<decltype(cpp2::is<int>(__expr)), std::false_type>) { if constexpr (std::is_same_v<decltype(cpp2::is<int>(__expr)), std::true_type>) { return calc<1,2>(); } else { if (cpp2::is<int>(__expr)) { return calc<1,2>(); } } } }
return 0;
}();
}

[[nodiscard]] auto main() -> int{
Expand Down
22 changes: 7 additions & 15 deletions regression-tests/test-results/mixed-type-safety-1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,13 @@ class Shape { public: virtual ~Shape() { } };
class Circle : public Shape { };
class Square : public Shape { };

//--- printing helpers -----------------
//--- printing setup -------------------

#line 13 "mixed-type-safety-1.cpp2"
auto print(cpp2::in<std::string> msg, auto const& x) -> void;


auto print(cpp2::in<std::string> msg, cpp2::in<bool> b) -> void;

#line 24 "mixed-type-safety-1.cpp2"
//--- examples -------------------------

#line 17 "mixed-type-safety-1.cpp2"
[[nodiscard]] auto main() -> int;


Expand All @@ -40,17 +36,13 @@ auto print(cpp2::in<std::string> msg, cpp2::in<bool> b) -> void;
auto print(cpp2::in<std::string> msg, auto const& x) -> void {
std::cout << msg << x << "\n"; }

auto print(cpp2::in<std::string> msg, cpp2::in<bool> b) -> void
{
cpp2::deferred_init<char const*> bmsg;
if (b) { bmsg.construct("true");}
else {bmsg.construct("false"); }
std::cout << msg << std::move(bmsg.value()) << "\n";
}

#line 26 "mixed-type-safety-1.cpp2"
#line 17 "mixed-type-safety-1.cpp2"
[[nodiscard]] auto main() -> int
{
std::cout << std::boolalpha;

//--- examples -------------------------

print("1.1 is int? ", cpp2::is<int>(1.1));
print( "1 is int? ", cpp2::is<int>(1));

Expand Down
Loading