dotnet / dotnet/dotnet-api-docs
Behavior of `constrained callvirt` should be documented with better clarity
- 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
Assessment
This issue has not been assessed yet.