dotnet / dotnet/dotnet-api-docs

Behavior of `constrained callvirt` should be documented with better clarity

Open
#12,359 1 comment 0 reactions 0 assignees View on GitHub
area-System.Reflection.Emit untriaged
Dominant language
C#
Stars
949
Forks
1.7k
Avg merge
3d 27m
Merged PRs (30d)
49

Description

### Type of issue

Missing information

### Description

In the documentation of `constrained` opcode, it says for `constrained.thisType callvirt.method`:

- The following should have been pushed onto the evaluation stack, in order: `ptr`, managed pointer to `thisType`; then `arg1` to `argN` of the arguments to `method`.
- If `thisType` is reference type, then `ptr` is dereferenced and passed as `this` to `method`.
- If `thisType` is value type and implements `method`, then `ptr` is passed as `this` to `method`.
- If `thisType` is value type and does not implement `method` (from `Object`, `ValueType`, `Enum`, or an `interface` with default implementation), then `ptr` is dereferenced, boxed, and passed as `this` to `method`.

From the text, **it is unclear when `ptr` is dereferenced (and the referent potentially boxed):**

1. Is it dereferenced when it is pushed onto the evaluation stack?
- Note that this choice is easily possible, thanks to conservative verification rules and generics sharing rules --- pushing of `ptr` and `constrained. callvirt.` must appear in the method body of a single method and generics are not shared for different value types, which allows JIT to know whether `ptr` is eventually dereferenced when compiling each (shared) instantiation of a method.
2. Is it dereferenced at the point of `constrained. callvirt.`?

The two options are **not equivalent**, because the referent of `ptr` could be changed after `ptr` has been pushed before reaching `constrained. callvirt.`.

- If `thisType` is reference type, this could change the `this` inside `method`.
- If `thisType` is value type and implements `method`, there is no difference.
- If `thisType` is value type and does not implement `method`, this could change *what value* is being boxed before entering `method`.

After some simple testing, I found on Windows 11 version 25H2, x64, .NET 10.0:

- If `thisType` is reference type, then `ptr` is dereferenced when it's pushed, which could be long before `constrained. callvirt.`.
- If `thisType` is value type and does not implement `method`, then `ptr` is dereferenced and boxed at `constrained. callvirt.`, which could be long after it's pushed.

Note that **the timings of dereference are different** for value types (not implementing `method`) and reference types.

The most typical use of `constrained. callvirt.` is C# generics, so naturally this behavior should easily enable C# spec 12.6.6.1 semantics. (Which, unfortunately, is also unclear on the timing of reading from a variable...)

### Page URL

https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.constrained?view=net-10.0#system-reflection-emit-opcodes-constrained

### Content source URL

https://github.com/dotnet/dotnet-api-docs/blob/main/xml/System.Reflection.Emit/OpCodes.xml

### Document Version Independent Id

9831712a-63bd-b93d-be05-0535facb360a

### Platform Id

530be95c-469c-3b4c-3a24-431f3aa53d66

### Article author

@dotnet-bot

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.