Skip to content

Type parameters named await, file, required, scoped or extension break the imposter #369

Closed

Description

@themidnightgospel

Summary

A type parameter named await, file, required, scoped or extension makes the generated imposter fail to compile. This holds for a target's type parameter and for a method's, and for interface and class targets alike:

public interface IService<@file> { @file Get(@file x); }
public interface IOther { @scoped M<@scoped>(@scoped x); System.Threading.Tasks.Task<@await> MAsync<@await>(@await x); }

Actual (master f4f09b8), by language version:

Name Fails from Errors Example
await C# 9 CS4003 ×6 _resultGenerator = async (await x) =>
file C# 11 about 360 public file x; reads as the file modifier (CS1519, CS9056)
required C# 11 about 360 public required x; reads as the required modifier (CS1519, CS9029)
scoped C# 11 44 (92 at C# 14) CS9062 "Types and aliases cannot be named 'scoped'", CS9048 (scoped x) =>
extension C# 14 2,076 public extension x; reads as an extension block (CS9281)

The C# 15 preview keywords fail the same way with the Roslyn 5.9 compiler: closed (170 errors) and union (1,145).

The other contextual keywords compile, 38 of them probed (async, dynamic, field, value, when, with, nameof, global, ...). So do var, _, record and partial, which #319 renames.

Cause

Generated code writes a type parameter's name bare. #109 escapes the reserved keywords, and #319 renames var, _, record and partial. These names are contextual keywords that generated code places where they read as a modifier, an extension block or an async lambda's await.

Proposed fix

Add them to the names TypeParameterNames.NameInGeneratedCode renames (NamesGeneratedCodeCantUse), the way #319 handles record and partial. Users never write type parameter names in setups, so the rename is invisible. Include closed and union now, so C# 15 needs no follow-up.

Escaping (@file) would also compile. But generated code takes type names from display strings, which escape only reserved keywords, so it would need a second mechanism next to #319's.

Add a compile test per name for a target's and a method's type parameter at C# 14.

Activity

  1. themidnightgospel commented on Oct 13, 2026

    @themidnightgospel
    OwnerAuthor

    Audit on master f4f09b8

    Verdict: real. Probed 49 names as a target type parameter (IKw<@name> with a method, an async method, an indexer and an event) and as a method type parameter (M<@name> sync and async), 98 compilations at LanguageVersion.Preview:

    name target type parameter method type parameter
    await CS4003 x2 CS4003 x2
    file CS1519, CS9056, CS0106 (58) CS1519, CS1031, CS8124 (95)
    required CS1519, CS9029, CS0106 (58) CS1519, CS1031, CS8124 (95)
    scoped CS9062, CS9048 (14) CS9062, CS9048 (54)
    extension CS9281 and 531 more Roslyn itself throws Unexpected value 'Extension' of type TypeKind while binding the generated file
    the other 44, incl. field, when, with, async, dynamic, nameof, global, yield, init, managed, unmanaged, notnull, nint, nuint, args, the LINQ and accessor words, allows, and/or/not, var, record, partial, value compile compile

    So the table in the issue is right, and the method-level extension shape is worse than an error count: the output is malformed enough that the compiler's own binder crashes on it. closed and union compile here because the test compiler is not 5.9; I cannot confirm those two rows.

    Better fix than a list

    NamesGeneratedCodeCantUse is a per-release list, and this issue is its second extension in two days (#319 added four names, this adds five plus two previews). Replace the list with the compiler's own knowledge:

    SyntaxFacts.GetContextualKeywordKind(name) != SyntaxKind.None || name == "_"
    • It is future-proof: SyntaxFacts binds at run time to the compiler that is running the generator, so closed and union are covered exactly when the compiler treats them as keywords, with no follow-up release.
    • It is a superset of today's list (var, record, partial are contextual keywords; _ is not, hence the explicit case) and of the five names here.
    • The extra renames (field, value, when, from, select, ...) are invisible to users, who pass type arguments positionally, and the probe shows they compile either way, so there is no behavioural cost; snapshot identity will show whether any existing test target uses such a name.

    If the list is kept instead, add _ and the C# 15 names as the issue says, but note the test matrix: file/required/scoped only fail from C# 11 and extension from C# 14, so the compile tests must run at C# 14 (Roslyn 4.14 and 5.0 in the matrix), as the issue already states.

    Tests

    One row per name at both levels, including the async method shape for await (the sync shape compiles). The 44 compiling names are a cheap negative control for the SyntaxFacts route: pick two or three (field, select) to pin that renaming them does no harm.

  2. added a commit that references this issue on Oct 13, 2026
    976f67a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions