Skip to content

Type loader support for static virtual methods - #71321

Merged
MichalStrehovsky merged 3 commits into
dotnet:mainfrom
MichalStrehovsky:svts
Jun 28, 2022
Merged

Type loader support for static virtual methods#71321
MichalStrehovsky merged 3 commits into
dotnet:mainfrom
MichalStrehovsky:svts

Conversation

@MichalStrehovsky

Copy link
Copy Markdown
Member

Fixes #67745.

Support for static virtual methods that was added in #66084 was enough for compile-time resolution of static virtual methods, but didn't cover dynamic code. For example, given following code:

interface IFoo { static virtual void Frob(); }
class SomeCaller<T> where T : IFoo { ... T.Frob(); }
class SomeClass : IFoo { ... }

If we do typeof(SomeCaller<>).MakeGenericType(typeof(SomeClass) at runtime, the runtime has to find what method implements IFoo.Frob on SomeClass and ensure proper data structures are generated for SomeCaller<SomeClass> so that the call lands in the right spot at runtime.

On a high level, what we need:

  • Change to the compiler to generate extra data ("interface dispatch maps") that lets us find an implementation of interface method X on a given type Y.
  • Change to the runtime to read the new data structure.
  • Change to the compiler to generate extra method bodies for types that can potentially be used with MakeGeneric at runtime. This is an overapproximation since we don't know the set of types that will really be used.
  • Change to type loader data structures to capture when shared generic code needs to do this mapping, and change the code in the compiler that emits it, and to the type loader that reads it.

I've made it so that the dispatch logic between instance and static methods is shared. It's not strictly necessary for both to go into the same data structure, but it prevents duplicating the code on the emission and reading side. The side effect of that is that static virtual methods now go into the sealed vtable. We have to put them somewhere. This spot is as good as any.

I've also had to make a small change to the ordering of data structure generation within the type loader. I've made is so that EEType/MethodTable structures are fully populated before we start filling out generic dictionaries. This prevents us from calling into the runtime dispatch logic with EETypes/MethodTables that are not actually built yet. I really didn't want to duplicate the dispatch logic into the type loader.

Cc @dotnet/ilc-contrib

Fixes dotnet#67745.

Support for static virtual methods that was added in dotnet#66084 was enough for compile-time resolution of static virtual methods, but didn't cover dynamic code. For example, given following code:

```csharp
interface IFoo { static virtual void Frob(); }
class SomeCaller<T> where T : IFoo { ... T.Frob(); }
class SomeClass : IFoo { ... }
```

If we do `typeof(SomeCaller<>).MakeGenericType(typeof(SomeClass)` at runtime, the runtime has to find what method implements `IFoo.Frob` on `SomeClass` and ensure proper data structures are generated for `SomeCaller<SomeClass>` so that the call lands in the right spot at runtime.

On a high level, what we need:
* Change to the compiler to generate extra data ("interface dispatch maps") that lets us find an implementation of interface method X on a given type Y.
* Change to the runtime to read the new data structure.
* Change to the compiler to generate extra method bodies for types that can potentially be used with MakeGeneric at runtime. This is an overapproximation since we don't know the set of types that will really be used.
* Change to type loader data structures to capture when shared generic code needs to do this mapping, and change the code in the compiler that emits it, and to the type loader that reads it.

I've made it so that the dispatch logic between instance and static methods is shared. It's not strictly necessary for both to go into the same data structure, but it prevents duplicating the code on the emission and reading side. The side effect of that is that static virtual methods now go into the sealed vtable. We have to put them somewhere. This spot is as good as any.

I've also had to make a small change to the ordering of data structure generation within the type loader. I've made is so that EEType/MethodTable structures are fully populated before we start filling out generic dictionaries. This prevents us from calling into the runtime dispatch logic with EETypes/MethodTables that are not actually built yet. I really didn't want to duplicate the dispatch logic into the type loader.
@Suchiman

Copy link
Copy Markdown
Contributor

Change to the compiler to generate extra data ("interface dispatch maps") that lets us find an implementation of interface method X on a given type Y.

Sounds like something that could be abused for GetInterfaceMap proper

@MichalStrehovsky

Copy link
Copy Markdown
Member Author

Change to the compiler to generate extra data ("interface dispatch maps") that lets us find an implementation of interface method X on a given type Y.

Sounds like something that could be abused for GetInterfaceMap proper

It's kind of already using it. The ResolveTarget call goes to the dispatch map. The underlying limitations in GetInterfaceMap implementation stem from the dispatch map being only useful to do dispatch at runtime, not for reflection discovery. (I.e. can't get info on abstract methods implementing the interface, for example.)

@MichalStrehovsky
MichalStrehovsky requested a review from jkotas June 28, 2022 01:36

@jkotas jkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@MichalStrehovsky
MichalStrehovsky merged commit 844b099 into dotnet:main Jun 28, 2022
@MichalStrehovsky
MichalStrehovsky deleted the svts branch June 28, 2022 04:38
@ghost ghost locked as resolved and limited conversation to collaborators Jul 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runtime type loader support for static abstract interface methods

3 participants