From 4e608f55e560abc9452d2e406ce8782663967c90 Mon Sep 17 00:00:00 2001 From: genisis0x Date: Thu, 14 May 2026 12:48:11 +0530 Subject: [PATCH] TypeChecker: don't ICE when duplicate user-defined operators are reached before the `using for` check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `TypeChecker::visit(UnaryOperation)` and `endVisit(BinaryOperation)` both asserted `m_errorReporter.hasErrors()` when more than one matching user-defined operator definition was found, relying on the duplicate check inside `endVisit(UsingForDirective)` (error 4705) to have already fired. That assumption breaks when a function body references the operator before the directive is fully type-checked — the duplicate is visible via the scope lookup, but no error has been emitted yet, so the assertion triggers an ICE. Drop the assertion. Picking the first match is harmless because the directive's duplicate check still runs and surfaces error 4705 to the user, halting compilation cleanly. Fixes #16616 Fixes #16636 --- libsolidity/analysis/TypeChecker.cpp | 22 ++++++++++++------- ...icate_binary_operator_self_referential.sol | 11 ++++++++++ ...licate_unary_operator_self_referential.sol | 11 ++++++++++ 3 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 test/libsolidity/syntaxTests/operators/userDefined/duplicate_binary_operator_self_referential.sol create mode 100644 test/libsolidity/syntaxTests/operators/userDefined/duplicate_unary_operator_self_referential.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 25bd4a915197..36f26e49d5d7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1632,10 +1632,13 @@ bool TypeChecker::visit(UnaryOperation const& _operation) resultType = builtinResult; else if (!matchingDefinitions.empty()) { - // This is checked along with `using for` directive but the error is not fatal. - if (matchingDefinitions.size() != 1) - solAssert(m_errorReporter.hasErrors()); - + // Duplicate user-defined operator bindings are flagged later by + // endVisit(UsingForDirective) (error 4705). When a function body that + // uses the operator is type-checked before the directive is fully + // processed (e.g. for self-referential `using for` definitions), no + // error has been emitted yet and the previous solAssert(hasErrors()) + // triggered an ICE. Pick the first match and let the directive's own + // duplicate check surface the error. operatorDefinition = *matchingDefinitions.begin(); } else @@ -1697,10 +1700,13 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) commonType = builtinResult.get(); else if (!matchingDefinitions.empty()) { - // This is checked along with `using for` directive but the error is not fatal. - if (matchingDefinitions.size() != 1) - solAssert(m_errorReporter.hasErrors()); - + // Duplicate user-defined operator bindings are flagged later by + // endVisit(UsingForDirective) (error 4705). When a function body that + // uses the operator is type-checked before the directive is fully + // processed (e.g. for self-referential `using for` definitions), no + // error has been emitted yet and the previous solAssert(hasErrors()) + // triggered an ICE. Pick the first match and let the directive's own + // duplicate check surface the error. operatorDefinition = *matchingDefinitions.begin(); // Set common type to the type used in the `using for` directive. diff --git a/test/libsolidity/syntaxTests/operators/userDefined/duplicate_binary_operator_self_referential.sol b/test/libsolidity/syntaxTests/operators/userDefined/duplicate_binary_operator_self_referential.sol new file mode 100644 index 000000000000..c9b71266f712 --- /dev/null +++ b/test/libsolidity/syntaxTests/operators/userDefined/duplicate_binary_operator_self_referential.sol @@ -0,0 +1,11 @@ +// Used to cause ICE in TypeChecker::endVisit(BinaryOperation) when a +// function body referenced an operator that had multiple matching +// `using for` definitions before the directive's own duplicate check +// (error 4705) had a chance to run. +type T is int256; +function sub(T a, T b) pure returns (T) {} +function add(T a, T b) pure returns (T) { T c = a + b; c; } +using {sub as +, add as +} for T global; +// ---- +// TypeError 4705: (267-270): User-defined binary operator + has more than one definition matching the operand type visible in the current scope. +// TypeError 4705: (277-280): User-defined binary operator + has more than one definition matching the operand type visible in the current scope. diff --git a/test/libsolidity/syntaxTests/operators/userDefined/duplicate_unary_operator_self_referential.sol b/test/libsolidity/syntaxTests/operators/userDefined/duplicate_unary_operator_self_referential.sol new file mode 100644 index 000000000000..536d50b15542 --- /dev/null +++ b/test/libsolidity/syntaxTests/operators/userDefined/duplicate_unary_operator_self_referential.sol @@ -0,0 +1,11 @@ +// Used to cause ICE in TypeChecker::visit(UnaryOperation) when a function +// body referenced an operator that had multiple matching `using for` +// definitions before the directive's own duplicate check (error 4705) +// had a chance to run. +type T is int256; +function neg1(T a) pure returns (T) { return -a; } +function neg2(T a) pure returns (T) { return a; } +using {neg1 as -, neg2 as -} for T global; +// ---- +// TypeError 4705: (262-266): User-defined unary operator - has more than one definition matching the operand type visible in the current scope. +// TypeError 4705: (273-277): User-defined unary operator - has more than one definition matching the operand type visible in the current scope.