Skip to content

TypeBuilder::GetConstructor and GetField type checks are in the wrong order #45988

Description

@eerhardt

There are 3 static methods on TypeBuilder to get an instantiated generic MemberInfo given a generic definition:

https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.typebuilder.getconstructor
https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.typebuilder.getfield
https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.typebuilder.getmethod

The issue is, the first 2 method (GetConstructor and GetField) have their checks in the wrong order, resulting in dead code. For example:

if (!(type is TypeBuilderInstantiation))
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(type));
// TypeBuilder G<T> ==> TypeBuilderInstantiation G<T>
if (type is TypeBuilder && type.IsGenericTypeDefinition)
type = type.MakeGenericType(type.GetGenericArguments());

The first check says "if the type isn't a TypeBuilderInstantiation, throw". Then the next check is checking if the type is a TypeBuilder. Both TypeBuilder and TypeBuilderInstantiation are sealed types that inherit from TypeInfo. So if the type isn't a TypeBuilderInstantiation, an exception is thrown, and the check for type is TypeBuilder is dead code.

I thought I could just remove the dead code from these two methods, but looking at the last one: TypeBuilder::GetMethod:

// The following converts from Type or TypeBuilder of G<T> to TypeBuilderInstantiation G<T>. These types
// both logically represent the same thing. The runtime displays a similar convention by having
// G<M>.M() be encoded by a typeSpec whose parent is the typeDef for G<M> and whose instantiation is also G<M>.
if (type.IsGenericTypeDefinition)
type = type.MakeGenericType(type.GetGenericArguments());
if (!(type is TypeBuilderInstantiation))
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(type));

This has the checks flipped, which makes more sense. If the type passed in IsGenericTypeDefinition (which can only be true if it is a TypeBuilder), then it calls MakeGenericType, which will return a TypeBuilderInstantiation.

My assumption is the GetConstructor and GetField methods were supposed to be written this way, but it was a mistake. I looked back in internal source history, and it appears all 3 of these methods were written this way when the feature was first developed.

If the code is truly not necessary, we should just delete the dead code. But I think the checks should be flipped to match the GetMethod method.

Note: we should also add unit tests for this scenario when fixing this bug.

Activity

  1. added
    help wanted[up-for-grabs] Good issue for external contributors
    and removed
    untriagedNew issue has not been triaged by the area owner
    on Jan 14, 2021
  2. added this to the 6.0.0 milestone on Jan 14, 2021
  3. BartoszKlonowski commented on May 23, 2021

    @BartoszKlonowski
    Contributor

    @eerhardt I would like to handle this issue, so please assign me to this item.
    CC: @steveharter @krwq

  4. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Jun 2, 2021
  5. removed
    help wanted[up-for-grabs] Good issue for external contributors
    on Jul 22, 2021
  6. steveharter commented on Aug 4, 2021

    @steveharter
    Contributor

    @eerhardt please move to v7 unless this the PR picked up ASAP.

  7. modified the milestones: 6.0.0, 7.0.0 on Aug 4, 2021
  8. eerhardt commented on Aug 4, 2021

    @eerhardt
    MemberAuthor

    There's no need for this to be fixed in 6.0.

  9. Repository owner moved this from vNext to Done in Triage POD for Reflection, META, etc.on Nov 2, 2021
  10. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Nov 2, 2021
  11. ghost locked as resolved and limited conversation to collaborators on Dec 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions