Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,13 @@ bool hasApplicableBooleanOperator(NamedTypeSymbol containingType, string name, T
var conversion = this.Conversions.ClassifyConversionFromType(argumentType, op.GetParameterType(0), isChecked: CheckOverflowAtRuntime, ref useSiteInfo);
if (conversion.IsImplicit)
{
// Operators defined on interfaces cannot be used with dynamic binding at runtime,
// so we don't consider them as applicable.
if (op.ContainingType.IsInterface)
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 4, 2025

Choose a reason for hiding this comment

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

if (op.ContainingType.IsInterface)

@copilot Please combine interface check with condition in if (type.Kind != SymbolKind.NamedType) on line 1580 instead. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 3cf6ed6. The interface check is now combined with the SymbolKind.NamedType check at line 1580, and the redundant check inside hasApplicableBooleanOperator has been removed.

{
continue;
}

@operator = op;
operators.Free();
return true;
Expand Down
93 changes: 93 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3649,6 +3649,99 @@ dynamic M(dynamic d)
);
}

[Fact]
public void DynamicBooleanExpression_InterfaceOperator_And()
{
const string source = @"
interface I1
{
static bool operator true(I1 x) => false;
static bool operator false(I1 x) => false;
}

class C1 : I1
{
public static C1 operator &(C1 x, C1 y) => x;
public static bool operator true(C1 x) => false;
public static bool operator false(C1 x) => false;
}

class Test
{
void M()
{
I1 x = new C1();
dynamic y = new C1();
_ = x && y;
}
}";
CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(
// (21,13): error CS7083: Expression must be implicitly convertible to Boolean or its type 'I1' must define operator 'false'.
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 4, 2025

Choose a reason for hiding this comment

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

Expression must be implicitly convertible to Boolean or its type 'I1' must define operator 'false'.

@copilot Please adjust corresponding resource string so that wording for this error message would be: "Expression must be implicitly convertible to Boolean or its type 'I1' must not be an interface and must define operator 'false'." #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit ee63d62. The error message now reads: "Expression must be implicitly convertible to Boolean or its type '{0}' must not be an interface and must define operator '{1}'." All test comments have been updated accordingly.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in commit ee63d62. The error message now reads: "Expression must be implicitly convertible to Boolean or its type '{0}' must not be an interface and must define operator '{1}'." All test comments have been updated accordingly.

@copilot Please adjust the following test comments in a similar way:

src\Compilers\CSharp\Test\Emit3\Semantics\ExtensionOperatorsTests.cs(15848):// (26,13): error CS7083: Expression must be implicitly convertible to Boolean or its type 'object' must define operator 'false'.
src\Compilers\CSharp\Test\IOperation\IOperation\IOperationTests_IBinaryOperatorExpression.cs(6353):// file.cs(6,18): error CS7083: Expression must be implicitly convertible to Boolean or its type 'C' must define operator 'false'.
src\Compilers\CSharp\Test\IOperation\IOperation\IOperationTests_IBinaryOperatorExpression.cs(6865):// file.cs(6,18): error CS7083: Expression must be implicitly convertible to Boolean or its type 'C?' must define operator 'false'.
src\Compilers\CSharp\Test\IOperation\IOperation\IOperationTests_IBinaryOperatorExpression.cs(7586):// file.cs(6,18): error CS7083: Expression must be implicitly convertible to Boolean or its type 'C?' must define operator 'false'.
src\Compilers\CSharp\Test\IOperation\IOperation\IOperationTests_IBinaryOperatorExpression.cs(7695):// file.cs(6,18): error CS7083: Expression must be implicitly convertible to Boolean or its type 'C?' must define operator 'false'.
src\Compilers\CSharp\Test\IOperation\IOperation\IOperationTests_IBinaryOperatorExpression.cs(8017):// file.cs(6,18): error CS7083: Expression must be implicitly convertible to Boolean or its type 'bool?' must define operator 'false'.
src\Compilers\CSharp\Test\Semantic\Semantics\OperatorTests.cs(9274):// (27,37): error CS7083: Expression must be implicitly convertible to Boolean or its type 'S1?' must define operator 'false'.
src\Compilers\CSharp\Test\Semantic\Semantics\OperatorTests.cs(9319):// (27,37): error CS7083: Expression must be implicitly convertible to Boolean or its type 'S1?' must define operator 'false'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 92e9427. Updated all 8 test comment occurrences across 3 files (ExtensionOperatorsTests.cs, IOperationTests_IBinaryOperatorExpression.cs, and OperatorTests.cs) to include "must not be an interface and" in the error message.

Diagnostic(ErrorCode.ERR_InvalidDynamicCondition, "x").WithArguments("I1", "false")
);
}

[Fact]
public void DynamicBooleanExpression_InterfaceOperator_Or()
{
const string source = @"
interface I1
{
static bool operator true(I1 x) => false;
static bool operator false(I1 x) => false;
}

class C1 : I1
{
public static C1 operator |(C1 x, C1 y) => x;
public static bool operator true(C1 x) => false;
public static bool operator false(C1 x) => false;
}

class Test
{
void M()
{
I1 x = new C1();
dynamic y = new C1();
_ = x || y;
}
}";
CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(
// (21,13): error CS7083: Expression must be implicitly convertible to Boolean or its type 'I1' must define operator 'true'.
Diagnostic(ErrorCode.ERR_InvalidDynamicCondition, "x").WithArguments("I1", "true")
);
}

[Fact]
public void DynamicBooleanExpression_ClassWithoutOperator()
{
// Ensure the class case still works correctly (regression test)
const string source = @"
class C0 { }

class C1 : C0
{
public static C1 operator &(C1 x, C1 y) => x;
public static bool operator true(C1 x) => false;
public static bool operator false(C1 x) => false;
}

class Test
{
void M()
{
C0 x = new C1();
dynamic y = new C1();
_ = x && y;
}
}";
CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(
// (17,13): error CS7083: Expression must be implicitly convertible to Boolean or its type 'C0' must define operator 'false'.
Diagnostic(ErrorCode.ERR_InvalidDynamicCondition, "x").WithArguments("C0", "false")
);
}

[Fact]
public void DynamicConstructorCall1()
{
Expand Down
Loading