Skip to content

Champion: Name lookup for simple name when target type known #2926

@gafter

Description

@gafter

There are three contexts in which lookup of a simple (not qualified) name would benefit from the fallback of looking up the identifier in a target type.

Simple ID expression

When an expression is an unqualified identifier, and lookup fails to find any accessible binding for that identifier, it is treated as target-typed. When the target type becomes available, the identifier is looked up in the context of the target type. If the identifier in that type designates an accessible constant, static field, or static property whose type is that target type, the identifier binds to that member.

Example:

enum Color { Red, Greed }

Color M1() => Red; // OK, binds to Color.Red

int IsRed(Color c) => c switch {
    Red => true, // OK, binds to Color.Red
    _ => false,
};

Object creation expression

When an object creation expression's type is given as an unqualified identifier (possibly with type arguments), and lookup fails to find any accessible binding for that identifier, it is treated as target-typed. When the target type becomes available, the identifier is looked up in the context of the target type (and the rest of the creation expression is bound, like the target-typed new expression). If the identifier in that type designates an accessible type that has an implicit reference conversion to that target type, the identifier binds to that type.

Example:

class Outer
{
    public class Inner1: Outer { }
    public class Inner2<T>: Outer { }
}

Outer M1() => new Inner1(); // binds to Outer.Inner1
Outer M2() => new Inner2<int>(); // binds to Outer.Inner2<T>

Type in a pattern

When the type appearing in a pattern is given as an unqualified identifier (possibly with type arguments), and lookup fails to find any accessible binding for that identifier, the identifier is looked up in the context of the pattern's input type. If the identifier in that type designates an accessible type, the identifier binds to that accessible type.

Example:

class Outer
{
    public class Inner1: Outer { }
    public class Inner2<T>: Outer { }
}

int M1(Outer o) => o switch
{
    Inner1 => 1,          // ok, binds to Outer.Inner1
    Inner2<int> => 2, // ok, binds to Outer.Inner2<T>
    _ => 3,
};
bool M2(Outer o) => o is Inner1;  // ok, binds to Outer.Inner1
bool M3(Outer o) => o is Inner2<int>;  // ok, binds to Outer.Inner2<T>

Design Meetings

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-26.md#discriminated-unions

Metadata

Metadata

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions