Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Oct 10, 2025

Problem

When using pointers in nested local functions inside iterators within unsafe classes, C# 12 reported a confusing diagnostic: "Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0". However, upgrading to C# 13 didn't fix the issue—instead, it showed "Pointers and fixed size buffers may only be used in an unsafe context". This created a misleading upgrade path.

Example:

unsafe class C
{
    IEnumerable<int> M()
    {
        yield return 1;
        local();
        void local()
        {
            int* p = null; // C# 12: suggests upgrade to C# 13
                          // C# 13: requires unsafe context
        }
    }
}

Root Cause

The issue stems from how iterators handle unsafe contexts between C# versions:

  • In C# 12 and below: iterators incorrectly inherit unsafe context from their containing scope (spec violation)
  • In C# 13+: iterators correctly establish a safe context

When the compiler detected pointer usage in nested local functions, it saw:

  1. We're in an unsafe region (inherited through the iterator in C# 12)
  2. We're indirectly in an iterator
  3. The feature is not available → report language version error

But this was misleading because upgrading to C# 13 wouldn't help—the iterator would establish a safe context, requiring an explicit unsafe modifier on the local function.

Solution

This PR fixes the diagnostic logic in two places:

  1. InMethodBinder.IsIndirectlyInIterator: Now correctly checks the parent binder chain for nested functions, ensuring we properly detect when code is indirectly within an iterator.

  2. Binder_Unsafe.GetUnsafeDiagnosticInfo: Improved to report ERR_UnsafeNeeded instead of ERR_FeatureNotAvailableInVersion12 when:

    • Code is indirectly in an iterator but not directly in one
    • The containing member is not itself an iterator
    • The unsafe context is inherited (not from an explicit unsafe modifier)

This provides users with the correct diagnostic in C# 12, matching what they'd see in C# 13, and clearly indicates that the solution is to add an unsafe modifier to the local function.

After this fix

unsafe class C
{
    IEnumerable<int> M()
    {
        yield return 1;
        local();
        void local()
        {
            int* p = null; // Both C# 12 & 13: CS0214 - requires unsafe context
        }
    }
}

The fix ensures consistent, helpful error messages across language versions and correctly guides users to the proper solution.

Fixes #[issue-number-from-original-issue]

Original prompt

This section details on the original issue you should resolve

<issue_title>Consider improving langversion diagnostics for "unsafe in iterators" feature</issue_title>
<issue_description>There are some confusing diagnostics where "feature is not supported in C# 12" is reported but if you upgrade to C# 13, you get an error "pointers must be used in unsafe context". It would be better if the latter error has been reported in C# 12 as well. This happens because unsafe/safe contexts changed slightly between C# 12 and 13, so in C# 12 the compiler thinks we are in unsafe context and you just need to upgrade to C# 13, but in C# 13 we are suddenly in safe context, so you get the unsafe error. For example:

unsafe class C
{
    System.Collections.Generic.IEnumerable<int> M()
    {
        int* p = null; // langversion error in C# 12; unsafe error in C# 13
        yield break;
    }
}

Similarly, there can be errors in C# 12 saying a construct (e.g., await or pointer) is not supported in an unsafe context. But if you upgrade to C# 13, that feature works since the context becomes safe. It would be better if we reported a langversion error in C# 12 (i.e., error saying "feature is not supported in C# 12", so users know they can just upgrade to make the error scenario work). For example:

unsafe class C
{
    System.Collections.Generic.IEnumerable<int> M()
    {
        var lam = () => sizeof(nint); // unsafe error in C# 12; works in C# 13
        yield break;
    }
}

Lastly, there are some unnecessary diagnostics in C# 12 where you get an error for every unsafe construct in an unsafe block inside an iterator even though you already got that error for the unsafe block itself, so the nested errors are just noise. Note that this is a preexisting behavior, only previously the errors said "cannot use unsafe in iterators" and now they say "unsafe in iterators is not supported in C# 12". They disappear completely in C# 13. Still, we could filter them out in C# 12, but that is tricky because we do not know if we are inside an unsafe block in an iterator (see M1 below) or if the unsafe context is just inherited from parent (in which case we cannot filter out the errors; see M2 below). For example:

unsafe class C
{
    System.Collections.Generic.IEnumerable<int> M1()
    {
        unsafe // langversion error in C# 12
        {
            int* p; // unnecessary langversion error in C# 12
        }
        yield break;
    }
    System.Collections.Generic.IEnumerable<int> M2()
    {
        int* p; // necessary langversion error in C# 12
        yield break;
    }
}

All of these could be solved by e.g., having separate BinderFlags for "unsafe region in C# 12" and "unsafe region in C# 13" that we could use in the compiler to decide which diagnostics to report. However, BinderFlags is uint currently and does not have any free slots.

Note: this issue will be referenced in tests (via #73046).

Related to #72662.</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #73280


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits October 11, 2025 00:00
- Fixed InMethodBinder.IsIndirectlyInIterator to check parent binder for nested functions
- Fixed GetUnsafeDiagnosticInfo to report ERR_UnsafeNeeded instead of language version error when unsafe context is inherited through iterator
- Added tests to verify the fix

Co-authored-by: jaredpar <146967+jaredpar@users.noreply.github.com>
Updated UnsafeArgsInIteratorLocalFuncs test to expect ERR_UnsafeNeeded instead of ERR_FeatureNotAvailableInVersion12 for pointer usage in nested contexts within iterators. This is the correct diagnostic because upgrading to C# 13 won't fix the issue (iterators will establish safe context).

Co-authored-by: jaredpar <146967+jaredpar@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] Improve diagnostics for unsafe in iterators feature Improve langversion diagnostics for "unsafe in iterators" feature Oct 11, 2025
@Copilot Copilot AI requested a review from jaredpar October 11, 2025 00:23
Copilot finished work on behalf of jaredpar October 11, 2025 00:23
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.

Consider improving langversion diagnostics for "unsafe in iterators" feature

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.