Merge pull request #9971 from ethereum/fix-modifiers-using-exprimental-features-from-calling-module

Checking experimental pragmas in the right source unit when compiling modifiers and inherited functions
This commit is contained in:
chriseth 2020-10-12 14:44:21 +02:00 committed by GitHub
commit 3724b98578
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 264 additions and 0 deletions

View File

@ -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.

View File

@ -78,6 +78,7 @@ public:
/// Update currently enabled set of experimental features.
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
std::set<ExperimentalFeature> const& experimentalFeaturesActive() const { return m_experimentalFeatures; }
/// @returns true if the given feature is enabled.
bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); }

View File

@ -1326,9 +1326,14 @@ void ContractCompiler::appendModifierOrFunctionCode()
if (codeBlock)
{
std::set<ExperimentalFeature> 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();

View File

@ -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

View File

@ -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

View File

@ -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()
{}
}
// ----

View File

@ -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 {}
}
// ----

View File

@ -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 {}
// ----

View File

@ -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 {}
// ----

View File

@ -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 {
_;
}
}
// ----

View File

@ -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) {}
}
// ----