From b287a6e99510028f853de7772fe3746ed17cb972 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 2 Nov 2020 14:10:37 +0100 Subject: [PATCH] Disallow specifying call options multiple times. --- Changelog.md | 1 + docs/080-breaking-changes.rst | 2 ++ libsolidity/analysis/TypeChecker.cpp | 24 +++++++++++++------ .../functionCall/call_options_overload.sol | 4 +++- .../salted_create_with_value.sol | 4 ++-- .../functionCalls/calloptions_repeated.sol | 11 ++++----- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Changelog.md b/Changelog.md index ce7ff99f6..65d11fdf4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Breaking Changes: * Assembler: The artificial ASSIGNIMMUTABLE opcode and the corresponding builtin in the "EVM with object access" dialect of Yul take the base offset of the code to modify as additional argument. * Code Generator: All arithmetic is checked by default. These checks can be disabled using ``unchecked { ... }``. * General: Remove global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``. + * Type Checker: Function call options can only be given once. * Type System: Unary negation can only be used on signed integers, not on unsigned integers. * Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type. * Parser: Exponentiation is right associative. ``a**b**c`` is parsed as ``a**(b**c)``. diff --git a/docs/080-breaking-changes.rst b/docs/080-breaking-changes.rst index 64b3c0029..b89d5d709 100644 --- a/docs/080-breaking-changes.rst +++ b/docs/080-breaking-changes.rst @@ -40,6 +40,8 @@ New Restrictions The previous behaviour was likely ambiguous. +* Function call options can only be given once, i.e. ``c.f{gas: 10000}{value: 1}()`` is invalid and has to be changed to ``c.f{gas: 10000, value: 1}()``. + * The global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4`` have been removed. These are low-level functions that were largely unused. Their behaviour can be accessed from inline assembly. \ No newline at end of file diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index fd5db0908..15d9bac5d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2425,14 +2425,24 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) return false; } - auto setCheckOption = [&](bool& _option, string const&& _name, bool _alreadySet = false) + if ( + expressionFunctionType->valueSet() || + expressionFunctionType->gasSet() || + expressionFunctionType->saltSet() + ) + m_errorReporter.typeError( + 1645_error, + _functionCallOptions.location(), + "Function call options have already been set, you have to combine them into a single " + "{...}-option." + ); + + auto setCheckOption = [&](bool& _option, string const& _name) { - if (_option || _alreadySet) + if (_option) m_errorReporter.typeError( 9886_error, _functionCallOptions.location(), - _alreadySet ? - "Option \"" + std::move(_name) + "\" has already been set." : "Duplicate option \"" + std::move(_name) + "\"." ); @@ -2446,7 +2456,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { if (kind == FunctionType::Kind::Creation) { - setCheckOption(setSalt, "salt", expressionFunctionType->saltSet()); + setCheckOption(setSalt, "salt"); expectType(*_functionCallOptions.options()[i], *TypeProvider::fixedBytes(32)); } else @@ -2484,7 +2494,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { expectType(*_functionCallOptions.options()[i], *TypeProvider::uint256()); - setCheckOption(setValue, "value", expressionFunctionType->valueSet()); + setCheckOption(setValue, "value"); } } else if (name == "gas") @@ -2499,7 +2509,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { expectType(*_functionCallOptions.options()[i], *TypeProvider::uint256()); - setCheckOption(setGas, "gas", expressionFunctionType->gasSet()); + setCheckOption(setGas, "gas"); } } else diff --git a/test/libsolidity/semanticTests/functionCall/call_options_overload.sol b/test/libsolidity/semanticTests/functionCall/call_options_overload.sol index 8938d8f0c..0881c5bac 100644 --- a/test/libsolidity/semanticTests/functionCall/call_options_overload.sol +++ b/test/libsolidity/semanticTests/functionCall/call_options_overload.sol @@ -5,8 +5,9 @@ contract C { v = this.f{value: 10}(2); x = this.f{gas: 1000}(2, 3); y = this.f{gas: 1000, value: 10}(2, 3); - z = this.f{gas: 1000}{value: 10}(2, 3); + z = this.f{value: 10, gas: 1000}(2, 3); } + function bal() external returns (uint) { return address(this).balance; } receive() external payable {} } // ==== @@ -14,3 +15,4 @@ contract C { // ---- // (), 1 ether // call() -> 1, 2, 2, 2 +// bal() -> 1000000000000000000 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index 16383f8b9..277ef14ff 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -12,8 +12,8 @@ contract B contract A { function f() public payable returns (uint, uint, uint) { B x = new B{salt: "abc", value: 3}(7); - B y = new B{value: 3}{salt: "abc"}(8); - B z = new B{value: 3, salt: "abc"}(9); + B y = new B{value: 3, salt: "abc"}(8); + B z = new B{salt: "abc", value: 3}(9); return (x.getBalance(), y.getBalance(), z.getBalance()); } } diff --git a/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol b/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol index 8185304d1..4356efcf9 100644 --- a/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol +++ b/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol @@ -11,9 +11,8 @@ contract C { // ==== // EVMVersion: >=constantinople // ---- -// TypeError 9886: (78-110): Option "gas" has already been set. -// TypeError 9886: (120-154): Option "gas" has already been set. -// TypeError 9886: (164-198): Option "value" has already been set. -// TypeError 9886: (208-249): Option "value" has already been set. -// TypeError 9886: (208-249): Option "gas" has already been set. -// TypeError 9886: (259-286): Option "salt" has already been set. +// TypeError 1645: (78-110): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (120-154): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (164-198): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (208-249): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (259-286): Function call options have already been set, you have to combine them into a single {...}-option.