-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
Compile and run the following code:
public class Program
{
static void Main()
{
var x1 = new S1();
var y1 = new S1();
_ = x1 && y1;
var x2 = new S2();
var y2 = new S2();
_ = x2 && y2;
}
}
struct S1 // nullable operators come first
{
public static S1 operator &(S1 x, S1 y) => x;
public static bool operator true(S1? x) => true;
public static bool operator false(S1? x)
{
System.Console.Write(1);
return false;
}
public static bool operator true(S1 x) => true;
public static bool operator false(S1 x) => false;
}
struct S2 // non-nullable operators come first
{
public static S2 operator &(S2 x, S2 y) => x;
public static bool operator true(S2 x) => true;
public static bool operator false(S2 x)
{
System.Console.Write(2);
return false;
}
public static bool operator true(S2? x) => true;
public static bool operator false(S2? x) => false;
}
Observed:
The code outputs "12", which means that compiler chooses between the two available false operators based on their order of declaration.
The language spec says the following:
Tshall contain declarations ofoperator trueandoperator false.
...- The operation
x && yis evaluated asT.false(x) ? x : T.&(x, y), whereT.false(x)is an invocation of theoperator falsedeclared inT, andT.&(x, y)is an invocation of the selectedoperator &. In other words,xis first evaluated andoperator falseis invoked on the result to determine ifxis definitely false. Then, ifxis definitely false, the result of the operation is the value previously computed forx. Otherwise,yis evaluated, and the selectedoperator &is invoked on the value previously computed forxand the value computed foryto produce the result of the operation.- The operation
x || yis evaluated asT.true(x) ? x : T.|(x, y), whereT.true(x)is an invocation of theoperator truedeclared inT, andT.|(x, y)is an invocation of the selectedoperator |. In other words,xis first evaluated andoperator trueis invoked on the result to determine ifxis definitely true. Then, ifxis definitely true, the result of the operation is the value previously computed forx. Otherwise,yis evaluated, and the selectedoperator |is invoked on the value previously computed forxand the value computed foryto produce the result of the operation.
So, it doesn't explicitly say how to resolve the ambiguity, but one might assume that regular unary operator overload resolution rules should be used.
BTW, It looks like for S1 compiler emits an invalid code, see #78609. Perhaps compiler should be looking for operator true and operator false that take exactly the type taken by operator &/operator |, or the type that the type taken by operator &/operator | converts to through an implicit reference conversion?