# C# Language Design Meeting for July 1st, 2026

## Agenda

- [Unsafe awaits](#unsafe-awaits)
- [Extension constants](#extension-constants)
- [Partial extension members](#partial-extension-members)

## Quote of the Day

- "My chat has not updated since 20 minutes ago" "Lucky you"

## Discussion

### Unsafe awaits

Champion issue: https://github.com/dotnet/csharplang/issues/9704  
Spec: https://github.com/dotnet/csharplang/blob/abe2b9c68eb4f153866b74c6c900b4c9ed03614a/proposals/unsafe-evolution.md#more-unsafe-contexts-and-relaxations

We started by returning to the interaction between `await` and `unsafe` contexts. In the broader unsafe evolution work,
pointer types themselves are no longer the thing that makes code unsafe. Instead, the unsafe boundary is meant to mark the
places where unmanaged memory is actually dereferenced or otherwise used in a memory-unsafe way. That means the old rule
that an `await` expression cannot appear inside an `unsafe` context no longer lines up well with the rest of the model.

The proposal is to allow `await` inside an `unsafe` context, while continuing to reject an `await` inside a `fixed`
statement. Today, the latter restriction falls out indirectly because `fixed` statements require an `unsafe` context and
`await` is banned there. Once `await` is permitted in `unsafe` contexts, the `fixed` restriction needs to become explicit:
we cannot preserve the pinning guarantee across a suspension point, so an `await` in a `fixed` body would not have sensible
semantics.

We discussed whether this relaxation creates a false sense that pointer values remain valid across an `await`. The answer
is intentionally no. Pointers remain the programmer's responsibility, and the compiler is not trying to track pointer
lifetime through suspension. This is different from the rules for `ref` locals and `ref struct` values, where the compiler
has a much stronger model and can reject uses after an `await` boundary. For pointers, the `unsafe` context is the audit
boundary, and the auditor must decide whether a pointer is still valid.

We also considered whether simply splitting the code into an `unsafe` block before the `await` and another one after the
`await` would be enough. That workaround is possible for simple cases, but it becomes awkward when the awaited expression
itself involves a call to a method that requires an unsafe context, or when the result is immediately used in a larger
expression. The relaxation avoids forcing users and fixers into those contortions for a case that is already covered by
the surrounding unsafe audit.

Two follow-up areas came up. First, we should separately investigate whether similar reasoning should relax restrictions
around `yield return` and around pointer or `ref` parameters in async methods and iterators. Second, we discussed whether
the runtime or BCL should have an `UnsafeTask`-like type whose `GetAwaiter` is itself unsafe, so a method could express
that the unsafe operation is delayed until the task is awaited. That is a library and runtime question rather than a
language requirement for this proposal.

Finally, we confirmed that this relaxation should be tied to language version, like the rest of the pointer relaxations,
and not to the memory safety rules version.

#### Conclusion

Approved. We will allow `await` inside `unsafe` contexts, continue to disallow `await` inside `fixed` statements, and gate
the relaxation on language version. We will investigate `yield return`, async and iterator parameters, and an
`UnsafeTask`-like BCL/runtime shape separately.

### Extension constants

Champion issue: https://github.com/dotnet/csharplang/issues/10242  
Spec: https://github.com/dotnet/csharplang/blob/7e2ffb61244316de0c9fc17a248f20b5cb302052/proposals/extension-constants.md

We next looked at extension constants. Extension blocks already support methods, properties, operators, and indexers, but
not constants. That leaves an awkward gap for APIs that want to add symbolic values to an existing type while preserving
compile-time constant semantics. A static property can be discovered through the extension surface, but it is not a
constant expression. A separate static constant is a constant expression, but it does not participate in extension member
lookup on the receiver type.

The proposed feature allows `const` declarations in extension blocks and makes them available through normal extension
member lookup. The constants remain ordinary constants in the important ways: they must have a valid constant type and
initializer, they are usable in constant-expression contexts, decimal constants continue to use the existing metadata
encoding, and non-constant fields remain disallowed in extension blocks.

Much of the discussion focused on whether approving constants alone is an artificial line, and whether we should instead
support static extension fields more generally. There was interest in that direction, particularly because users can often
simulate static fields through static properties or other indirection today. However, fields raise additional metadata and
representation questions that constants avoid. In particular, a general static field in a generic extension block would
need a storage location that can represent the relevant type parameters, while implementation members for extension
properties can be ordinary generic methods. Constants do not have the same storage problem.

We also discussed whether `using static` should bring extension constants into scope. The current extension model requires
a member access through a receiver or extended type, and extension constants do not change that. That means a pattern such
as `using static Math` does not translate directly to extension constants, but this is consistent with other static
extension members and was not considered a blocker.

Another concern was the usual constant-versioning problem: a constant value is baked into the consuming assembly and does
not change until recompilation. Extension constants can add one more way for a later member on the underlying type to
change what newly compiled code binds to. However, extension methods and other extension members already have a related
versioning shape: once an extension member has been chosen by a compiled assembly, adding an instance member later does
not redirect that existing assembly without recompilation. The constant-specific value-baking issue is different, but not
different or severe enough to block the feature.

The strongest motivating scenarios were simple ones such as enum polyfills, where a library wants to make a missing
constant-like member available on an existing type. The group was comfortable with that usefulness and with leaving static
extension fields for a separate, fuller proposal.

#### Conclusion

Approved. Extension constants are worth adding on their own. Static extension fields remain an interesting future proposal,
but are not part of this feature.

### Partial extension members

Champion issue: https://github.com/dotnet/csharplang/issues/10227  
Spec: https://github.com/dotnet/csharplang/blob/7e2ffb61244316de0c9fc17a248f20b5cb302052/proposals/partial-extension-members.md

We closed with partial extension members. Classic extension methods can already be partial, which is useful for source
generator scenarios where user code declares the shape of a method and generated code provides the implementation. The
extension block syntax currently loses that ability, even though one of the design goals for extension blocks was that a
classic extension method should be mechanically translatable to an equivalent block-style extension member. That creates a
regression for existing partial extension method patterns and blocks some source generator scenarios, including regex and
WPF-style patterns that want generated implementation members.

The basic proposal is to allow partial members inside extension blocks when the enclosing static class is partial. Existing
partial member rules carry over: there is one definition part and at most one implementation part, older erasable partial
methods remain erasable, and modern partial members with accessibility modifiers remain non-erasable and require an
implementation. The definition part remains the source of the caller-facing signature.

We discussed how much freedom the two parts should have to differ. For ordinary partial methods, parameter names and some
other details can differ between definition and implementation, with the definition providing the names visible to callers.
For extension members, the receiver parameter and any extension block type parameters add another layer: the member's two
parts might appear in different extension blocks, and those blocks may have marker and grouping metadata that must be
merged or matched. The proposal treats the extension parameter and extension block type parameters like the corresponding
method parameters for matching purposes, but this led to concerns that the model may be too permissive or too implicit.

Attributes were the most subtle part of that discussion. Ordinary partial methods merge attributes from the definition and
implementation parts. For extension members, receiver attributes live on the extension block, not directly on each member,
so merging them can become surprising. If a definition and implementation appear in the same extension block, naively
applying both extension-block attributes to the lowered receiver parameter could duplicate attributes in metadata. If the
parts appear in different blocks, merging receiver attributes may be desirable for compatibility with ordinary partial
methods, but it also makes extension block merging more visible and more complex.

We considered whether the feature should require an explicit `partial` modifier on the extension block itself. That would
make block merging explicit, and could let the language say that all members in those blocks are affected by the merge, not
just the partial members that happen to be matched. It would also provide a clearer place to define how receiver
attributes and block signatures merge. This is a more syntactic change than the current proposal, which keeps extension
blocks themselves non-partial, but it may make the semantics easier to reason about.

Despite those details, the group was aligned on the underlying feature. We want partial extension members so that extension
block syntax does not block partial and source-generator patterns that work with classic extension methods. On the
matching question, we had a slight lean toward requiring the relevant extension block signatures to match rather than
trying to merge differences, but we did not make a final decision. The spec still needs a future design pass on extension
block signatures, receiver attributes, and metadata.

#### Conclusion

Approved. Partial extension members should be supported. We have a slight preference for a matching-based approach to the
receiver and extension block questions, but the exact rules remain unresolved and need to be brought back.
