Skip to content

Commit

Permalink
Add order-independent type definitions
Browse files Browse the repository at this point in the history
Add order-independent type definitions, including order-independent dependencies between type definitions - see new test case code, pasted below in this commit message for convenience

Note: This looks in pretty good shape, but it's a significant restructuring in the way the code is lowered to Cpp1, so there will probably be some corner cases to tweak in follow-on commits

There are now three (not two) Cpp1 lowering passes:
1. type forward declarations (new)
2. type definitions (new) and function forward declarations (existing)
3. function definitions, including type-scope/member function out-of-line definitions (new) and non-type-scope functions (existing)

When compiling `.h2` files, phases 1 and 2 go into the generated `.h` and phase 3 goes into the generated `.hpp`

Comments written in Cpp2 all go on an entity's definition, because there are no forward declarations. So when lowering to Cpp1, comments are now split into two groups to put them where a reader of the Cpp1 code would naturally expect to see them:
   - comments not within function bodies are emitted in phase 2, where they belong on the types and function declarations (new)
   - comments within function bodies are emitted in phase 3, likewise where they belong

Also:

- Added support for `out` parameters on constructors. For example, this is useful when safely creating pointer cycles among types that refer to each other, respecting Cpp2's guaranteed program-meaningful initialization safety rules (demonstrated in the test case in this commit, pasted below)

- Added support for `_` as the name of an anonymous object. This supports uses of RAII guards such as `_ := std::lock_guard(my_mutex);` and `_ := finally( :()= print("done"); ); );` - see example in test case below

- Removed unused `offset` variable, closes #309

### Regression test to demonstrate code that now works... convenience copy of `regression-tests\pure2-types-order-independence-and-nesting.cpp2`

```
N: namespace = {

//  A type X, that uses Y (defined later)
//
X: type = {
    py: *Y;

    //  Note: A constructor with an 'out' parameter
    operator=: (out this, out y: Y) = {
        //  === The following comments will stay close to, but not exactly at,
        //      the corresponding lines that get moved to the Cpp1 mem-init-list

        //  At this point, 'this' is now in scope and can safely be used.
        //  The guaranteed initialization rules ensure we cannot use an
        //  unconstructed object, and if a constructor that has begun chooses to
        //  hand out 'this&' (as in the next line below) then it is explicitly
        //  aware it's doing so - this constructor knows that if y's constructor
        //  uses the pointer it will see 'this' object's state as of this line

        //  'out' parameters are constructed first
        y = this&;  // ok, construct 'y' to refer to 'this'
        //  at this point, 'y' is now initialized and can safely be used

        //  then members are constructed next
        py = y&;    // ok, set 'this' to refer to 'y'

        //  === from here onward, the comments stick with their code

        //  then do anything else the constructor wants to do
        std::cout << "made a safely initialized cycle\n";
    }

    //  X::exx member function description here
    exx: (this, count: int) = {
        //  Exercise '_' anonymous objects too while we're at it
        _ := cpp2::finally( :()= std::cout << "leaving call to 'why((count)$)'\n"; );
        if count < 5 {
            py*.why( count+1 );     // use Y object from X
        }
    }
}

//  Another type Y, that uses X
//
Y: type = {
    px: *X;

    operator=: (out this, x: *X) = px = x;

    why: (this, count: int) =
        px*.exx( count+1 );         // use X object from Y
}

M: namespace = {

//  Testing nested templated types
A: <T, U> type = {
    B: <I: int> type = {
        f: <V, J: int, W> (w: W) = std::cout << "hallo (w)$\n";
    }
}

}

}

//  Mainline - gratuitous comment just to check that this comment
//  stays on the function declaration when lowering
main: () =
{
    y: N::Y;            // declare an uninitialized Y object
    x: N::X = (out y);  // construct y and x, and point them at each other

    // now have the two objects call each other back and forth a few times
    x.exx(1);

    // and test a nested template out-of-line definition
    N::M::A<int, int>::B<42>::f<int, 43>("welt");
}
```
  • Loading branch information
hsutter committed Apr 1, 2023
1 parent b370ab8 commit 45fb75e
Show file tree
Hide file tree
Showing 83 changed files with 2,057 additions and 859 deletions.
20 changes: 3 additions & 17 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ class out {
}

auto construct(auto&& ...args) -> void {
if (has_t) {
if (has_t || called_construct()) {
Default.expects( t );
*t = T(std::forward<decltype(args)>(args)...);
}
Expand All @@ -587,7 +587,7 @@ class out {
}

auto construct_list(auto&& ...args) -> void {
if (has_t) {
if (has_t || called_construct()) {
Default.expects( t );
*t = T{std::forward<decltype(args)>(args)...};
}
Expand Down Expand Up @@ -631,21 +631,7 @@ class out {
#endif


// === NOTE: (see also corresponding note in cppfront.cpp)
//
// This "&" capture default is to work around 'if constexpr' bugs in Clang and MSVC...
// If we don't emit a not-actually-used "[&]" here, then both compilers get tripped up
// if we attempt UFCS for a member function name... the reason is that the UFCS macro
// generates a lambda that does an 'if constexpr' to call the member function if
// available, but Clang and MSVC look into the NOT-taken 'if constexpr' branch (which
// they shouldn't) and see the nonmember function call syntax and think it's trying to
// use the member function with implicit 'this->' and choke... (see issue #281, and
// https://godbolt.org/z/M47zzMsoT for a distilled repro)
//
// The workaround that seems to be effective is to add TWO actually-unused "&" captures:
// - here, and
// - and also in the cppfront.cpp build_capture_lambda_intro_for() code
//
// Note: [&] is because a nested UFCS might be viewed as trying to capture 'this'

#define CPP2_UFCS(FUNCNAME,PARAM1,...) \
[&](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <iostream>

main: () -> int = {
vec: std::vector<std::string>
vec: std::vector<std::string>
= ("hello", "2022");
view: std::span = vec;

Expand All @@ -16,7 +16,7 @@ main: () -> int = {
:(inout x:_) = x += "-ish";
);

// Initializating from a function expression
// Initializing from a function expression
callback := :(inout x:_) = x += " maybe";
std::for_each(
view.begin(),
Expand Down
80 changes: 80 additions & 0 deletions regression-tests/pure2-types-order-independence-and-nesting.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

N: namespace = {

// A type X, that uses Y (defined later)
//
X: type = {
py: *Y;

// Note: A constructor with an 'out' parameter
operator=: (out this, out y: Y) = {
// === The following comments will stay close to, but not exactly at,
// the corresponding lines that get moved to the Cpp1 mem-init-list

// At this point, 'this' is now in scope and can safely be used.
// The guaranteed initialization rules ensure we cannot use an
// unconstructed object, and if a constructor that has begun chooses to
// hand out 'this&' (as in the next line below) then it is explicitly
// aware it's doing so - this constructor knows that if y's constructor
// uses the pointer it will see 'this' object's state as of this line

// 'out' parameters are constructed first
y = this&; // ok, construct 'y' to refer to 'this'
// at this point, 'y' is now initialized and can safely be used

// then members are constructed next
py = y&; // ok, set 'this' to refer to 'y'

// === from here onward, the comments stick with their code

// then do anything else the constructor wants to do
std::cout << "made a safely initialized cycle\n";
}

// X::exx member function description here
exx: (this, count: int) = {
// Exercise '_' anonymous objects too while we're at it
_ := cpp2::finally( :()= std::cout << "leaving call to 'why((count)$)'\n"; );
if count < 5 {
py*.why( count+1 ); // use Y object from X
}
}
}

// Another type Y, that uses X
//
Y: type = {
px: *X;

operator=: (out this, x: *X) = px = x;

why: (this, count: int) =
px*.exx( count+1 ); // use X object from Y
}

M: namespace = {

// Testing nested templated types
A: <T, U> type = {
B: <I: int> type = {
f: <V, J: int, W> (w: W) = std::cout << "hallo (w)$\n";
}
}

}

}

// Mainline - gratuitous comment just to check that this comment
// stays on the function declaration when lowering
main: () =
{
y: N::Y; // declare an uninitialized Y object
x: N::X = (out y); // construct y and x, and point them at each other

// now have the two objects call each other back and forth a few times
x.exx(1);

// and test a nested template out-of-line definition
N::M::A<int, int>::B<42>::f<int, 43>("welt");
}
Original file line number Diff line number Diff line change
@@ -1,121 +1,121 @@
mixed-float-literals.cpp2:3:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:13:3: warning: expression result unused [-Wunused-value]
123;
^~~
mixed-float-literals.cpp2:4:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:14:3: warning: expression result unused [-Wunused-value]
123u;
^~~~
mixed-float-literals.cpp2:5:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:15:3: warning: expression result unused [-Wunused-value]
123ul;
^~~~~
mixed-float-literals.cpp2:6:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:16:3: warning: expression result unused [-Wunused-value]
123ull;
^~~~~~
mixed-float-literals.cpp2:7:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:17:3: warning: expression result unused [-Wunused-value]
123l;
^~~~
mixed-float-literals.cpp2:8:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:18:3: warning: expression result unused [-Wunused-value]
123ll;
^~~~~
mixed-float-literals.cpp2:9:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:19:3: warning: expression result unused [-Wunused-value]
123'456ll;
^~~~~~~~~
mixed-float-literals.cpp2:10:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:20:3: warning: expression result unused [-Wunused-value]
123;
^~~
mixed-float-literals.cpp2:11:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:21:3: warning: expression result unused [-Wunused-value]
123U;
^~~~
mixed-float-literals.cpp2:12:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:22:3: warning: expression result unused [-Wunused-value]
123UL;
^~~~~
mixed-float-literals.cpp2:13:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:23:3: warning: expression result unused [-Wunused-value]
123ULL;
^~~~~~
mixed-float-literals.cpp2:14:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:24:3: warning: expression result unused [-Wunused-value]
123L;
^~~~
mixed-float-literals.cpp2:15:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:25:3: warning: expression result unused [-Wunused-value]
123LL;
^~~~~
mixed-float-literals.cpp2:16:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:26:3: warning: expression result unused [-Wunused-value]
123'456LL;
^~~~~~~~~
mixed-float-literals.cpp2:18:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:28:3: warning: expression result unused [-Wunused-value]
123'456.0f;
^~~~~~~~~~
mixed-float-literals.cpp2:19:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:29:3: warning: expression result unused [-Wunused-value]
123'456.f;
^~~~~~~~~
mixed-float-literals.cpp2:20:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:30:3: warning: expression result unused [-Wunused-value]
456.0;
^~~~~
mixed-float-literals.cpp2:21:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:31:3: warning: expression result unused [-Wunused-value]
456.;
^~~~
mixed-float-literals.cpp2:22:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:32:3: warning: expression result unused [-Wunused-value]
1.0e10;
^~~~~~
mixed-float-literals.cpp2:23:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:33:3: warning: expression result unused [-Wunused-value]
1.0e+10;
^~~~~~~
mixed-float-literals.cpp2:24:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:34:3: warning: expression result unused [-Wunused-value]
1.0e-10;
^~~~~~~
mixed-float-literals.cpp2:25:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:35:3: warning: expression result unused [-Wunused-value]
1.0e-10f;
^~~~~~~~
mixed-float-literals.cpp2:26:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:36:3: warning: expression result unused [-Wunused-value]
1.e-10;
^~~~~~
mixed-float-literals.cpp2:27:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:37:3: warning: expression result unused [-Wunused-value]
1.e-10f;
^~~~~~~
mixed-float-literals.cpp2:28:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:38:3: warning: expression result unused [-Wunused-value]
1e-10;
^~~~~
mixed-float-literals.cpp2:29:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:39:3: warning: expression result unused [-Wunused-value]
1e-10f;
^~~~~~
mixed-float-literals.cpp2:30:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:40:3: warning: expression result unused [-Wunused-value]
1e-1'0;
^~~~~~
mixed-float-literals.cpp2:31:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:41:3: warning: expression result unused [-Wunused-value]
123'456.0F;
^~~~~~~~~~
mixed-float-literals.cpp2:32:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:42:3: warning: expression result unused [-Wunused-value]
123'456.F;
^~~~~~~~~
mixed-float-literals.cpp2:33:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:43:3: warning: expression result unused [-Wunused-value]
456.0;
^~~~~
mixed-float-literals.cpp2:34:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:44:3: warning: expression result unused [-Wunused-value]
456.;
^~~~
mixed-float-literals.cpp2:35:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:45:3: warning: expression result unused [-Wunused-value]
1.0E10;
^~~~~~
mixed-float-literals.cpp2:36:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:46:3: warning: expression result unused [-Wunused-value]
1.0E+10;
^~~~~~~
mixed-float-literals.cpp2:37:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:47:3: warning: expression result unused [-Wunused-value]
1.0E-10;
^~~~~~~
mixed-float-literals.cpp2:38:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:48:3: warning: expression result unused [-Wunused-value]
1.0E-10F;
^~~~~~~~
mixed-float-literals.cpp2:39:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:49:3: warning: expression result unused [-Wunused-value]
1.E-10;
^~~~~~
mixed-float-literals.cpp2:40:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:50:3: warning: expression result unused [-Wunused-value]
1.E-10F;
^~~~~~~
mixed-float-literals.cpp2:41:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:51:3: warning: expression result unused [-Wunused-value]
1E-10;
^~~~~
mixed-float-literals.cpp2:42:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:52:3: warning: expression result unused [-Wunused-value]
1E-10F;
^~~~~~
mixed-float-literals.cpp2:43:3: warning: expression result unused [-Wunused-value]
mixed-float-literals.cpp:53:3: warning: expression result unused [-Wunused-value]
1E-1'0;
^~~~~~
mixed-float-literals.cpp2:59:3: warning: expression result unused [-Wunused-value]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
made a safely initialized cycle
leaving call to 'why(5)'
leaving call to 'why(3)'
leaving call to 'why(1)'
hallo welt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
made a safely initialized cycle
leaving call to 'why(5)'
leaving call to 'why(3)'
leaving call to 'why(1)'
hallo welt
Empty file.
14 changes: 10 additions & 4 deletions regression-tests/test-results/mixed-as-for-variant-20-types.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@

#line 1 "mixed-as-for-variant-20-types.cpp2"
template<int I>
struct X { operator int() const { return I; } };

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"


//=== Cpp2 type definitions and function declarations ===========================

template<int I>
struct X { operator int() const { return I; } };

#line 4 "mixed-as-for-variant-20-types.cpp2"
[[nodiscard]] auto main() -> int;

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


#line 4 "mixed-as-for-variant-20-types.cpp2"
Expand Down
12 changes: 9 additions & 3 deletions regression-tests/test-results/mixed-bounds-check.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@

#line 1 "mixed-bounds-check.cpp2"

#include <vector>
//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"


//=== Cpp2 type definitions and function declarations ===========================


#include <vector>

#line 4 "mixed-bounds-check.cpp2"
[[nodiscard]] auto main() -> int;

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


#line 4 "mixed-bounds-check.cpp2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@


//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"


//=== Cpp2 type definitions and function declarations ===========================


#line 2 "mixed-bounds-safety-with-assert-2.cpp2"
[[nodiscard]] auto main() -> int;

Expand All @@ -13,7 +20,7 @@ auto add_42_to_subrange(auto& rng, cpp2::in<int> start, cpp2::in<int> end) -> vo
#include <span>
#include <iostream>

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


#line 2 "mixed-bounds-safety-with-assert-2.cpp2"
Expand Down
Loading

0 comments on commit 45fb75e

Please sign in to comment.