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

Allow unsafe blocks in iterators #73046

Merged

Conversation

jjonescz
Copy link
Member

@jjonescz jjonescz commented Apr 16, 2024

Test plan: #72662
Speclet: https://github.com/dotnet/csharplang/blob/main/proposals/ref-unsafe-in-iterators-async.md
Speclet update: dotnet/csharplang#8056

  • Unsafe blocks are allowed in iterators.
  • yield return is disallowed in unsafe blocks.
  • "address of" operator is disallowed in iterators.
  • Iterator bodies start a safe context even if nested in an unsafe context (this is already in the spec but wasn't implemented properly I think; this PR implements it from C# 13+).
    • This is necessary otherwise iterators in unsafe classes could never contain yield returns as those are now disallowed in unsafe contexts. Plus it would be a breaking change as iterators are allowed in unsafe classes in C# 12.
  • If an iterator method is itself marked unsafe that could be a separate error but that would result in unnecessary duplicate errors in most scenarios ("unsafe iterator is disallowed" and "yield return is disallowed in unsafe context"), so the current proposal and implementation allows it, but the unsafe modifier does not affect the iterator body, only the signature (and set accessors in case of properties).

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Apr 16, 2024
@jjonescz jjonescz force-pushed the RefInAsync-03-UnsafeInIterators branch from cdb5a49 to badd0a9 Compare April 17, 2024 10:37
@jjonescz jjonescz marked this pull request as ready for review April 17, 2024 11:55
@jjonescz jjonescz requested a review from a team as a code owner April 17, 2024 11:55
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 30, 2024

        if (!unsafeClass || !unsafeOperator)

&&? #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1187 in 4817390. [](commit_id = 4817390, deletion_comment = False)

@jjonescz
Copy link
Member Author

        if (!unsafeClass || !unsafeMethod)

Yes, thanks, I will fix it up - the same is repeated across several theories.


In reply to: 2085479313


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:991 in 4817390. [](commit_id = 4817390, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 30, 2024

        if (!unsafeClass || !unsafeOperator)

&&? #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1225 in 4817390. [](commit_id = 4817390, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 30, 2024

        CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics();

Since we do not expect any differences in behavior for C# 12, consider verifying behavior for that language version too. This is applicable to UnsafeContext_Method_Signature_Safe, UnsafeContext_Method_Body_Unsafe and UnsafeContext_Method_Body_Safe as well. And to similar tests for other declarations kinds. #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1002 in 4817390. [](commit_id = 4817390, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 30, 2024

            // (5,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.

This error doesn't really allow us to observe the fact that the context is unsafe. Consider using the following body instead:

                        yield return local();
                        
                        int local()
                        {
                            return sizeof(nint);
                        }
``` #Closed

---
Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1155 in 4817390. [](commit_id = 48173903995a7bc276c19dfebf530ae30d5eb5b9, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 30, 2024

        if (!unsafeClass || !unsafeIndexer)

&& #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1389 in 4817390. [](commit_id = 4817390, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 30, 2024

    public void UnsafeContext_Indexer_Iterator_Body_CSharp13_Unsafe(bool unsafeClass, bool unsafeIndexer)

Safe? #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1580 in d0977cb. [](commit_id = d0977cb, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 30, 2024

    public void UnsafeContext_Property_Iterator_Body_CSharp13_Unsafe(bool unsafeClass, bool unsafeProperty)

Safe? #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1831 in d0977cb. [](commit_id = d0977cb, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 16)

@jjonescz jjonescz requested a review from AlekseyTs April 30, 2024 15:31
Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 19), assuming CI is passing

return (this.Flags.Includes(BinderFlags.UnsafeRegion) || !modifiers.Any(SyntaxKind.UnsafeKeyword))
? this
: new Binder(this, this.Flags | BinderFlags.UnsafeRegion);
// In C# 13 and above, iterator bodies define a safe context even when nested in an unsafe context.
Copy link
Member

@cston cston Apr 30, 2024

Choose a reason for hiding this comment

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

In C# 13 and above,

Aren't iterator bodies defined in a safe context, regardless of language version? #Resolved

Copy link
Member Author

@jjonescz jjonescz Apr 30, 2024

Choose a reason for hiding this comment

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

Directly in iterators it might appear so because using any unsafe constructs is an error anyway, but if we use local functions for example, it can be seen that in C# 12 iterators do not define safe context, for example:

unsafe class C // unsafe context
{
    System.Collections.Generic.IEnumerable<int> M() // iterator
    {
        yield return local();
        int local() => sizeof(nint); // allowed, we are in unsafe context
    }
}

This is a pre-existing spec violation.

? this
: new Binder(this, this.Flags | BinderFlags.UnsafeRegion);
// In C# 13 and above, iterator bodies define a safe context even when nested in an unsafe context.
// In C# 12 and below, we keep the behavior that nested iterator bodies (e.g., local functions)
Copy link
Member

@cston cston Apr 30, 2024

Choose a reason for hiding this comment

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

nested iterator bodies

Does this mean nested functions within iterator methods, or nested functions that are also iterators, or something else? (It looks like nested iterators are defined in a safe context in C#12 - see sharplab.io.) #Resolved

Copy link
Member Author

@jjonescz jjonescz Apr 30, 2024

Choose a reason for hiding this comment

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

nested iterator bodies

Hm, that's a weird phrase, I don't know why I wrote that. In C# 12 basically only nested local functions inherit unsafe context. I will rewrite the comment to say that.

It looks like nested iterators are defined in a safe context in C# 12

You're right, iterators always appear to be in a safe context due to there being errors for unsafe constructs in iterators. But in reality, they do not remove the UnsafeRegion binder flag, so they are in a safe context as can be seen in nested non-iterator local functions (see my comment above).

@cston
Copy link
Member

cston commented Apr 30, 2024

    public void UnsafeContext_Operator_Body_Safe()

"SafeContext_"? #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1304 in 2fd98b5. [](commit_id = 2fd98b5, deletion_comment = False)

@cston
Copy link
Member

cston commented Apr 30, 2024

    public void UnsafeContext_Operator_Iterator_Signature_Safe()

"SafeContext_"? Same comment for next test. (I don't feel strongly about the names, but it's not clear whether "UnsafeContext_" and "SafeContext_" apply to the use (or not) of the unsafe keyword.) #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1369 in 8fc5539. [](commit_id = 8fc5539, deletion_comment = False)

@cston
Copy link
Member

cston commented Apr 30, 2024

    public void UnsafeContext_Property_Iterator_Signature_UnsafeIndexer(bool unsafeClass)

"Property"? #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1845 in 2fd98b5. [](commit_id = 2fd98b5, deletion_comment = False)

@jjonescz
Copy link
Member Author

jjonescz commented May 2, 2024

    public void UnsafeContext_Operator_Iterator_Signature_Safe()

UnsafeContext_ is supposed to group all tests related to testing unsafe context behavior (how it affects signatures, bodies, how it's inherited). In particular, the unsafe context might be actually missing in the test. The _Safe/_Unsafe at the end of the test name should signify whether the body/signature is safe/unsafe in the particular test. I now see how this can be confusing, but I'm not sure how to make it better - I would like to keep some common prefix for these tests, but if you have a better name suggestion in mind, feel free to share it; I will gladly change this in a follow-up to make it clearer.


In reply to: 2086258970


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:1369 in 8fc5539. [](commit_id = 8fc5539, deletion_comment = False)

@jjonescz jjonescz enabled auto-merge (squash) May 2, 2024 08:16
@jjonescz jjonescz merged commit 83bd82f into dotnet:features/RefInAsync May 2, 2024
24 checks passed
@jjonescz jjonescz deleted the RefInAsync-03-UnsafeInIterators branch May 2, 2024 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers New Feature - Ref/Unsafe in Iterators/Async untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants