Skip to content

Conversation

@sitio-couto
Copy link
Collaborator

@sitio-couto sitio-couto commented Nov 2, 2023

Stack from ghstack (oldest at bottom):

This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

struct Node {
  Node *next;
};
void test(struct Node n) {}

Generates:

!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>

To generate the Node struct type, its members must be created first.
However, the next member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the Node type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
next member of a Node value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the next
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
cir.struct type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>

Summary of the changes made:

  • Named records are now uniquely identified by their name. An attempt
    to create a new record with the same will fail.

  • Anonymous records are uniquely identified by members and other
    relevant attributes.

  • StructType has a new mutate method that allows it to be mutated
    after it has been created. Each type can only be mutated if it is
    identified and incomplete, rendering further changes impossible.

  • When building a new name StructType, the builder will try to first
    create, then complete the type, ensuring that:

    • Inexistent types are created
    • Existing incomplete types are completed
    • Existing complete types with matching attributes are reused
    • Existing complete types with different attributes raise errors
  • StructType now uses the CyclicParser/Printer guard to avoid infinite
    recursion and identify when it should print/parse a self-reference.

This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

[ghstack-poisoned]
sitio-couto added a commit that referenced this pull request Nov 2, 2023
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: bdee2a3
Pull Request resolved: #303
@sitio-couto
Copy link
Collaborator Author

@gitoleg this might interest you!

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Cool, thanks for improving this!

This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

[ghstack-poisoned]
sitio-couto added a commit that referenced this pull request Nov 3, 2023
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: d138d70
Pull Request resolved: #303
Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

LGTM with one minor comment.

This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

[ghstack-poisoned]
sitio-couto added a commit that referenced this pull request Nov 3, 2023
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: 58a270f
Pull Request resolved: #303
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

[ghstack-poisoned]
sitio-couto added a commit that referenced this pull request Nov 3, 2023
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
@sitio-couto sitio-couto merged commit 10a6422 into gh/sitio-couto/2/base Nov 3, 2023
sitio-couto added a commit that referenced this pull request Nov 3, 2023
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
@sitio-couto sitio-couto deleted the gh/sitio-couto/2/head branch November 3, 2023 22:35
bcardosolopes pushed a commit that referenced this pull request Nov 22, 2023
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
lanza pushed a commit that referenced this pull request Dec 20, 2023
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Dec 20, 2023
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
lanza pushed a commit that referenced this pull request Jan 29, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Jan 29, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
lanza pushed a commit that referenced this pull request Mar 23, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Mar 23, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
eZWALT pushed a commit to eZWALT/clangir that referenced this pull request Mar 24, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to llvm#303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close llvm#256 as well
eZWALT pushed a commit to eZWALT/clangir that referenced this pull request Mar 24, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: llvm#303
eZWALT pushed a commit to eZWALT/clangir that referenced this pull request Mar 24, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to llvm#303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close llvm#256 as well
lanza pushed a commit that referenced this pull request Apr 29, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Apr 29, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
lanza pushed a commit that referenced this pull request Apr 29, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Apr 29, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
eZWALT pushed a commit to eZWALT/clangir that referenced this pull request Apr 29, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: llvm#303
eZWALT pushed a commit to eZWALT/clangir that referenced this pull request Apr 29, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to llvm#303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close llvm#256 as well
lanza pushed a commit that referenced this pull request Apr 29, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Apr 29, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
pysuxing pushed a commit to pysuxing/llvm-project that referenced this pull request Jul 17, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f650515cbf2d7f6e27d45aae6f768ba44f92
Pull Request resolved: llvm/clangir#303
bruteforceboy pushed a commit to bruteforceboy/clangir that referenced this pull request Oct 2, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to llvm#303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close llvm#256 as well
Hugobros3 pushed a commit to shady-gang/clangir that referenced this pull request Oct 2, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: llvm#303
Hugobros3 pushed a commit to shady-gang/clangir that referenced this pull request Oct 2, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to llvm#303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close llvm#256 as well
keryell pushed a commit to keryell/clangir that referenced this pull request Oct 19, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: llvm#303
keryell pushed a commit to keryell/clangir that referenced this pull request Oct 19, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to llvm#303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close llvm#256 as well
lanza pushed a commit that referenced this pull request Nov 5, 2024
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Nov 5, 2024
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
lanza pushed a commit that referenced this pull request Mar 18, 2025
This allows a named StructType to be mutated after it has been created,
if it is identified and incomplete.

The motivation for this is to improve the codegen of CIR in certain
scenarios where an incomplete type is used and later completed. These
usually leave the IR in an inconsistent state, where there are two
records types with the same identifier but different definitions (one
complete the other incomplete).

For example:

```c++
struct Node {
  Node *next;
};
void test(struct Node n) {}
```

Generates:

```mlir
!temp_struct = !cir.struct<struct "Node" incomplete>
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}>
```

To generate the `Node` struct type, its members must be created first.
However, the `next` member is a recursive reference, so it can only be
completed after its parent. This generates a temporary incomplete
definition of the `Node` type that remains in the code even after the
type to which it refers is completed. As a consequence, accessing the
`next` member of a `Node` value fetches the old incomplete version of
the type which affects CIR's type-checking capabilities.

This patch ensures that, once the parent is fully visited, the `next`
member can be completed in place, automatically updating any references
to it at a low cost. To represent recursive types, the StructType now
is equipped with self-references. These are represented by a
`cir.struct` type with just the name of the parent struct that it
refers to. The same snippet of code will not generate the following
CIR IR:

```mlir
!full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}>
```

Summary of the changes made:
 - Named records are now uniquely identified by their name. An attempt
   to create a new record with the same will fail.
 - Anonymous records are uniquely identified by members and other
   relevant attributes.
 - StructType has a new `mutate` method that allows it to be mutated
   after it has been created. Each type can only be mutated if it is
   identified and incomplete, rendering further changes impossible.
 - When building a new name StructType, the builder will try to first
   create, then complete the type, ensuring that:

    - Inexistent types are created
    - Existing incomplete types are completed
    - Existing complete types with matching attributes are reused
    - Existing complete types with different attributes raise errors

 - StructType now uses the CyclicParser/Printer guard to avoid infinite
   recursion and identify when it should print/parse a self-reference.

ghstack-source-id: a6d4f65
Pull Request resolved: #303
lanza pushed a commit that referenced this pull request Mar 18, 2025
I think it's time to claim that CIR supports recursive types (many
thanks to #303 and to @sitio-couto :) )
And we can bring back the `get_member` verification back, with no checks
for incomplete types. What do you think?

And we can close #256 as well
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.

3 participants