From 3128e82a9aa0996087986b357440ea4bd500ef5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 6 Oct 2020 16:32:05 +0200 Subject: [PATCH] Look for experimental pragmas in the module containing the definition when generating code for modifiers and inherited functions --- Changelog.md | 1 + libsolidity/codegen/CompilerContext.h | 1 + libsolidity/codegen/ContractCompiler.cpp | 5 ++ ...2_in_function_inherited_in_v1_contract.sol | 32 ++++++++++++ ...ode_v2_in_modifier_used_in_v1_contract.sol | 38 ++++++++++++++ .../abiEncoder/v1_call_to_v2_modifier.sol | 27 ++++++++++ .../v1_constructor_with_v2_modifier.sol | 33 ++++++++++++ ...ance_from_contract_calling_v2_function.sol | 25 +++++++++ ...itance_from_contract_emitting_v2_event.sol | 21 ++++++++ .../v1_modifier_overriding_v2_modifier.sol | 29 +++++++++++ .../abiEncoder/v1_v2_v1_modifier_mix.sol | 52 +++++++++++++++++++ 11 files changed, 264 insertions(+) create mode 100644 test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol create mode 100644 test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol diff --git a/Changelog.md b/Changelog.md index 185ff1eb3..69e9bcbc2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: Bugfixes: * Code generator: Fix internal compiler error when referencing members via module name but not using the reference. + * Code generator: Fix ``ABIEncoderV2`` pragma from the current module affecting inherited functions and applied modifiers. * Type Checker: Fix internal compiler error caused by storage parameters with nested mappings in libraries. * Name Resolver: Fix shadowing/same-name warnings for later declarations. diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 72ed4fe6f..bd37c3686 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -78,6 +78,7 @@ public: /// Update currently enabled set of experimental features. void setExperimentalFeatures(std::set const& _features) { m_experimentalFeatures = _features; } + std::set const& experimentalFeaturesActive() const { return m_experimentalFeatures; } /// @returns true if the given feature is enabled. bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 0cf650736..f69999e1f 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1326,9 +1326,14 @@ void ContractCompiler::appendModifierOrFunctionCode() if (codeBlock) { + std::set experimentalFeaturesOutside = m_context.experimentalFeaturesActive(); + m_context.setExperimentalFeatures(codeBlock->sourceUnit().annotation().experimentalFeatures); + m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight()); codeBlock->accept(*this); + m_context.setExperimentalFeatures(experimentalFeaturesOutside); + solAssert(!m_returnTags.empty(), ""); m_context << m_returnTags.back().first; m_returnTags.pop_back(); diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol new file mode 100644 index 000000000..5c074032b --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol @@ -0,0 +1,32 @@ +==== Source: A ==== +pragma experimental ABIEncoderV2; + +struct Data { + uint a; + uint[2] b; + uint c; +} + +contract A { + function get() public view returns (Data memory) { + return Data(5, [uint(66), 77], 8); + } +} + +contract B { + function foo(A _a) public returns (uint) { + return _a.get().b[1]; + } +} +==== Source: B ==== +import "A"; + +contract C is B { + function test() public returns (uint) { + return foo(new A()); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 77 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol new file mode 100644 index 000000000..a6ac945a7 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol @@ -0,0 +1,38 @@ +==== Source: A ==== +pragma experimental ABIEncoderV2; + +struct Data { + uint value; +} + +contract A { + function get() public view returns (Data memory) { + return Data(5); + } +} + +contract B { + uint x = 10; + uint y = 10; + + modifier updateStorage() { + A a = new A(); + x = a.get().value; + _; + y = a.get().value; + } +} +==== Source: B ==== +import "A"; + +contract C is B { + function test() + public + updateStorage + returns (uint, uint) + { + return (x, y); + } +} +// ---- +// test() -> 5, 10 diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol new file mode 100644 index 000000000..2096c4174 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol @@ -0,0 +1,27 @@ +==== Source: A ==== +pragma experimental ABIEncoderV2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + modifier validate() { + A(0x00).get(); + _; + } +} +==== Source: B ==== +import "A"; + +contract C is B { + function foo() + public + validate() + {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol new file mode 100644 index 000000000..7c3b3b98f --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol @@ -0,0 +1,33 @@ +==== Source: A ==== +pragma experimental ABIEncoderV2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + constructor() validate { + A(0x00).get(); + } + + modifier validate() { + A(0x00).get(); + _; + } +} + +==== Source: B ==== +import "A"; + +contract C is B {} +==== Source: C ==== +import "B"; + +contract D is C { + constructor() validate B() validate C() validate {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol new file mode 100644 index 000000000..ddf8650f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol @@ -0,0 +1,25 @@ +==== Source: A ==== +pragma experimental ABIEncoderV2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + constructor() { + A(0x00).get(); + } + + function foo() public view { + A(0x00).get(); + } +} +==== Source: B ==== +import "A"; + +contract C is B {} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol new file mode 100644 index 000000000..42a13138f --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +pragma experimental ABIEncoderV2; + +struct Item { + uint x; +} + +library L { + event Ev(Item); +} + +contract C { + function foo() public { + emit L.Ev(Item(1)); + } +} +==== Source: B ==== +import "A"; + +contract D is C {} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol new file mode 100644 index 000000000..9d9bfccfb --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol @@ -0,0 +1,29 @@ +==== Source: A ==== +pragma experimental ABIEncoderV2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + modifier validate() virtual { + A(0x00).get(); + _; + } +} + +==== Source: B ==== +import "A"; + +contract C is B { + function foo() public pure validate {} + + modifier validate() override { + _; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol new file mode 100644 index 000000000..a0aa13f5d --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol @@ -0,0 +1,52 @@ +==== Source: C ==== +import "X"; +import "V1A"; +import "V2A"; +import "V1B"; + +contract C is V1A, V2A, V1B { + function foo() + public + modV1A + modV2A // There should be no error for modV2A (it uses ABIEncoderV2) + modV1B + { + } +} +==== Source: V1A ==== +import "X"; + +contract V1A { + modifier modV1A() { + _; + } +} +==== Source: V1B ==== +import "X"; + +contract V1B { + modifier modV1B() { + _; + } +} +==== Source: V2A ==== +pragma experimental ABIEncoderV2; +import "X"; + +contract V2A { + modifier modV2A() { + X(0x00).get(); + _; + } +} +==== Source: X ==== +pragma experimental ABIEncoderV2; + +struct Data { + bool flag; +} + +contract X { + function get() public view returns (Data memory) {} +} +// ----