Merge pull request #6456 from ethereum/fixFunctionRegistration

[Yul] Fix registration of functions in scopes.
This commit is contained in:
chriseth 2019-04-05 15:52:25 +02:00 committed by GitHub
commit ef3a18999c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 257 additions and 220 deletions

View File

@ -22,6 +22,7 @@ Compiler Features:
Bugfixes: Bugfixes:
* SMTChecker: Implement Boolean short-circuiting. * SMTChecker: Implement Boolean short-circuiting.
* SMTChecker: SSA control-flow did not take into account state variables that were modified inside inlined functions that were called inside branches. * SMTChecker: SSA control-flow did not take into account state variables that were modified inside inlined functions that were called inside branches.
* Yul: Properly register functions and disallow shadowing between function variables and variables in the outside scope.

View File

@ -42,11 +42,11 @@ bool Scope::registerVariable(YulString _name, YulType const& _type)
return true; return true;
} }
bool Scope::registerFunction(YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns) bool Scope::registerFunction(YulString _name, std::vector<YulType> _arguments, std::vector<YulType> _returns)
{ {
if (exists(_name)) if (exists(_name))
return false; return false;
identifiers[_name] = Function{_arguments, _returns}; identifiers[_name] = Function{std::move(_arguments), std::move(_returns)};
return true; return true;
} }

View File

@ -56,8 +56,8 @@ struct Scope
bool registerLabel(YulString _name); bool registerLabel(YulString _name);
bool registerFunction( bool registerFunction(
YulString _name, YulString _name,
std::vector<YulType> const& _arguments, std::vector<YulType> _arguments,
std::vector<YulType> const& _returns std::vector<YulType> _returns
); );
/// Looks up the identifier in this or super scopes and returns a valid pointer if found /// Looks up the identifier in this or super scopes and returns a valid pointer if found

View File

@ -74,28 +74,13 @@ bool ScopeFiller::operator()(VariableDeclaration const& _varDecl)
bool ScopeFiller::operator()(FunctionDefinition const& _funDef) bool ScopeFiller::operator()(FunctionDefinition const& _funDef)
{ {
bool success = true;
vector<Scope::YulType> arguments;
for (auto const& _argument: _funDef.parameters)
arguments.emplace_back(_argument.type.str());
vector<Scope::YulType> returns;
for (auto const& _return: _funDef.returnVariables)
returns.emplace_back(_return.type.str());
if (!m_currentScope->registerFunction(_funDef.name, arguments, returns))
{
//@TODO secondary location
m_errorReporter.declarationError(
_funDef.location,
"Function name " + _funDef.name.str() + " already taken in this scope."
);
success = false;
}
auto virtualBlock = m_info.virtualBlocks[&_funDef] = make_shared<Block>(); auto virtualBlock = m_info.virtualBlocks[&_funDef] = make_shared<Block>();
Scope& varScope = scope(virtualBlock.get()); Scope& varScope = scope(virtualBlock.get());
varScope.superScope = m_currentScope; varScope.superScope = m_currentScope;
m_currentScope = &varScope; m_currentScope = &varScope;
varScope.functionScope = true; varScope.functionScope = true;
bool success = true;
for (auto const& var: _funDef.parameters + _funDef.returnVariables) for (auto const& var: _funDef.parameters + _funDef.returnVariables)
if (!registerVariable(var, _funDef.location, varScope)) if (!registerVariable(var, _funDef.location, varScope))
success = false; success = false;
@ -153,10 +138,9 @@ bool ScopeFiller::operator()(Block const& _block)
// an entry in the scope according to their visibility. // an entry in the scope according to their visibility.
for (auto const& s: _block.statements) for (auto const& s: _block.statements)
if (s.type() == typeid(FunctionDefinition)) if (s.type() == typeid(FunctionDefinition))
if (!boost::apply_visitor(*this, s)) if (!registerFunction(boost::get<FunctionDefinition>(s)))
success = false; success = false;
for (auto const& s: _block.statements) for (auto const& s: _block.statements)
if (s.type() != typeid(FunctionDefinition))
if (!boost::apply_visitor(*this, s)) if (!boost::apply_visitor(*this, s))
success = false; success = false;
@ -178,6 +162,26 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const&
return true; return true;
} }
bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef)
{
vector<Scope::YulType> arguments;
for (auto const& _argument: _funDef.parameters)
arguments.emplace_back(_argument.type.str());
vector<Scope::YulType> returns;
for (auto const& _return: _funDef.returnVariables)
returns.emplace_back(_return.type.str());
if (!m_currentScope->registerFunction(_funDef.name, std::move(arguments), std::move(returns)))
{
//@TODO secondary location
m_errorReporter.declarationError(
_funDef.location,
"Function name " + _funDef.name.str() + " already taken in this scope."
);
return false;
}
return true;
}
Scope& ScopeFiller::scope(Block const* _block) Scope& ScopeFiller::scope(Block const* _block)
{ {
auto& scope = m_info.scopes[_block]; auto& scope = m_info.scopes[_block];

View File

@ -73,6 +73,7 @@ private:
langutil::SourceLocation const& _location, langutil::SourceLocation const& _location,
Scope& _scope Scope& _scope
); );
bool registerFunction(FunctionDefinition const& _funDef);
Scope& scope(Block const* _block); Scope& scope(Block const* _block);

View File

@ -198,6 +198,7 @@ void OptimiserSuite::run(
BlockFlattener{}(ast); BlockFlattener{}(ast);
DeadCodeEliminator{}(ast); DeadCodeEliminator{}(ast);
FunctionGrouper{}(ast);
VarNameCleaner{ast, *_dialect, reservedIdentifiers}(ast); VarNameCleaner{ast, *_dialect, reservedIdentifiers}(ast);
yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, ast); yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, ast);

View File

@ -38,7 +38,7 @@ struct Dialect;
* renumbered by their base name. * renumbered by their base name.
* Function names are not modified. * Function names are not modified.
* *
* Prerequisites: Disambiguator, FunctionHoister * Prerequisites: Disambiguator, FunctionHoister, FunctionGrouper
*/ */
class VarNameCleaner: public ASTModifier class VarNameCleaner: public ASTModifier
{ {

View File

@ -343,7 +343,7 @@ printTask "Testing assemble, yul, strict-assembly and optimize..."
# while it results in empty binary representation with optimizations turned on. # while it results in empty binary representation with optimizations turned on.
test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ let x:u256 := 0:u256 }" "--yul" test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ let x:u256 := 0:u256 }" "--yul"
test_solc_assembly_output "{ let x := 0 }" "{ let x := 0 }" "--strict-assembly" test_solc_assembly_output "{ let x := 0 }" "{ let x := 0 }" "--strict-assembly"
test_solc_assembly_output "{ let x := 0 }" "{ }" "--strict-assembly --optimize" test_solc_assembly_output "{ let x := 0 }" "{ { } }" "--strict-assembly --optimize"
) )

View File

@ -4,18 +4,22 @@
Pretty printed source: Pretty printed source:
object "MyContract" { object "MyContract" {
code { code {
{
sstore(0, caller()) sstore(0, caller())
let _1 := datasize("Runtime") let _1 := datasize("Runtime")
datacopy(0, dataoffset("Runtime"), _1) datacopy(0, dataoffset("Runtime"), _1)
return(0, _1) return(0, _1)
} }
}
object "Runtime" { object "Runtime" {
code { code {
{
mstore(0, sload(0)) mstore(0, sload(0))
return(0, 0x20) return(0, 0x20)
} }
} }
} }
}
Binary representation: Binary representation:

View File

@ -1 +1 @@
{"contracts":{"A":{"object":{"evm":{"assembly":" /* \"A\":17:18 */\n 0x00\n 0x00\n /* \"A\":11:19 */\n mload\n /* \"A\":20:40 */\n sstore\n","bytecode":{"linkReferences":{},"object":"600060005155","opcodes":"PUSH1 0x0 PUSH1 0x0 MLOAD SSTORE ","sourceMap":""}},"ir":"object \"object\" {\n code {\n let x := mload(0)\n sstore(add(x, 0), 0)\n }\n}\n","irOptimized":"object \"object\" {\n code {\n sstore(mload(0), 0)\n }\n}\n"}}},"errors":[{"component":"general","formattedMessage":"Yul is still experimental. Please use the output with care.","message":"Yul is still experimental. Please use the output with care.","severity":"warning","type":"Warning"}]} {"contracts":{"A":{"object":{"evm":{"assembly":" /* \"A\":17:18 */\n 0x00\n 0x00\n /* \"A\":11:19 */\n mload\n /* \"A\":20:40 */\n sstore\n","bytecode":{"linkReferences":{},"object":"600060005155","opcodes":"PUSH1 0x0 PUSH1 0x0 MLOAD SSTORE ","sourceMap":""}},"ir":"object \"object\" {\n code {\n let x := mload(0)\n sstore(add(x, 0), 0)\n }\n}\n","irOptimized":"object \"object\" {\n code {\n {\n sstore(mload(0), 0)\n }\n }\n}\n"}}},"errors":[{"component":"general","formattedMessage":"Yul is still experimental. Please use the output with care.","message":"Yul is still experimental. Please use the output with care.","severity":"warning","type":"Warning"}]}

View File

@ -4,9 +4,11 @@
Pretty printed source: Pretty printed source:
object "object" { object "object" {
code { code {
{
let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun() let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun()
let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun() let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun()
sstore(a1, a2) sstore(a1, a2)
}
function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3 function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3
{ {
let a := 1 let a := 1

View File

@ -390,6 +390,12 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case_different_literal)
BOOST_CHECK(successParse("{ switch 1:u256 case \"1\":u256 {} case \"2\":u256 {} }")); BOOST_CHECK(successParse("{ switch 1:u256 case \"1\":u256 {} case \"2\":u256 {} }"));
} }
BOOST_AUTO_TEST_CASE(function_shadowing_outside_vars)
{
CHECK_ERROR("{ let x:u256 function f() -> x:u256 {} }", DeclarationError, "already taken in this scope");
BOOST_CHECK(successParse("{ { let x:u256 } function f() -> x:u256 {} }"));
}
BOOST_AUTO_TEST_CASE(builtins_parser) BOOST_AUTO_TEST_CASE(builtins_parser)
{ {
struct SimpleDialect: public Dialect struct SimpleDialect: public Dialect

View File

@ -1073,6 +1073,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// let _1 := mload(1) // let _1 := mload(1)
// let _2 := mload(0) // let _2 := mload(0)
// if slt(sub(_1, _2), 64) // if slt(sub(_1, _2), 64)
@ -1085,6 +1086,7 @@
// sstore(x3, x2) // sstore(x3, x2)
// sstore(1, x4) // sstore(1, x4)
// pop(abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(mload(30), mload(31), mload(32), mload(33), mload(34), mload(35), mload(36), mload(37), mload(38), mload(39), mload(40), mload(41))) // pop(abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(mload(30), mload(31), mload(32), mload(33), mload(34), mload(35), mload(36), mload(37), mload(38), mload(39), mload(40), mload(41)))
// }
// function abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(headStart, dataEnd) -> value0, value1, value2, value3, value4 // function abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(headStart, dataEnd) -> value0, value1, value2, value3, value4
// { // {
// if slt(sub(dataEnd, headStart), 128) // if slt(sub(dataEnd, headStart), 128)

View File

@ -459,6 +459,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// let _1 := 0x20 // let _1 := 0x20
// let _2 := 0 // let _2 := 0
// let _3 := mload(_2) // let _3 := mload(_2)
@ -515,6 +516,7 @@
// sstore(calldataload(_6), calldataload(add(_6, _1))) // sstore(calldataload(_6), calldataload(add(_6, _1)))
// sstore(value2, value3) // sstore(value2, value3)
// sstore(_2, pos) // sstore(_2, pos)
// }
// function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset, end) -> array // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset, end) -> array
// { // {
// if iszero(slt(add(offset, 0x1f), end)) // if iszero(slt(add(offset, 0x1f), end))

View File

@ -232,6 +232,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// mstore(0x80, 7673901602397024137095011250362199966051872585513276903826533215767972925880) // mstore(0x80, 7673901602397024137095011250362199966051872585513276903826533215767972925880)
// mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375) // mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375)
// let notes := add(0x04, calldataload(0x04)) // let notes := add(0x04, calldataload(0x04))
@ -333,6 +334,7 @@
// } // }
// mstore(i_1, 0x01) // mstore(i_1, 0x01)
// return(i_1, 0x20) // return(i_1, 0x20)
// }
// function validatePairing(t2) // function validatePairing(t2)
// { // {
// let t2_x := calldataload(t2) // let t2_x := calldataload(t2)

View File

@ -20,6 +20,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// let _1 := 0x40 // let _1 := 0x40
// mstore(_1, add(mload(_1), 0x20)) // mstore(_1, add(mload(_1), 0x20))
// let p := mload(_1) // let p := mload(_1)
@ -27,3 +28,4 @@
// mstore(add(p, 96), 2) // mstore(add(p, 96), 2)
// mstore(_1, 0x20) // mstore(_1, 0x20)
// } // }
// }

View File

@ -13,5 +13,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// mstore(1, 1) // mstore(1, 1)
// } // }
// }

View File

@ -35,6 +35,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) // let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1))
// let a_1, b_1 := abi_decode_t_bytes_calldata_ptr(a, b) // let a_1, b_1 := abi_decode_t_bytes_calldata_ptr(a, b)
// let a_2, b_2 := abi_decode_t_bytes_calldata_ptr(a_1, b_1) // let a_2, b_2 := abi_decode_t_bytes_calldata_ptr(a_1, b_1)
@ -43,6 +44,7 @@
// let a_5, b_5 := abi_decode_t_bytes_calldata_ptr(a_4, b_4) // let a_5, b_5 := abi_decode_t_bytes_calldata_ptr(a_4, b_4)
// let a_6, b_6 := abi_decode_t_bytes_calldata_ptr(a_5, b_5) // let a_6, b_6 := abi_decode_t_bytes_calldata_ptr(a_5, b_5)
// mstore(a_6, b_6) // mstore(a_6, b_6)
// }
// function abi_decode_t_bytes_calldata_ptr(offset, end) -> arrayPos, length // function abi_decode_t_bytes_calldata_ptr(offset, end) -> arrayPos, length
// { // {
// if iszero(slt(add(offset, 0x1f), end)) // if iszero(slt(add(offset, 0x1f), end))

View File

@ -13,6 +13,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// let a := mload(0) // let a := mload(0)
// let b := mload(1) // let b := mload(1)
// if mload(2) // if mload(2)
@ -22,3 +23,4 @@
// } // }
// mstore(a, b) // mstore(a, b)
// } // }
// }

View File

@ -10,5 +10,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// mstore(9, 0) // mstore(9, 0)
// } // }
// }

View File

@ -11,5 +11,7 @@
// step: fullSuite // step: fullSuite
// ---- // ----
// { // {
// {
// mstore(10, 0) // mstore(10, 0)
// } // }
// }

View File

@ -4,22 +4,22 @@
y := mload(x) y := mload(x)
let a, b := f(x, y) let a, b := f(x, y)
sstore(a, b) sstore(a, b)
function f(t, v) -> x, y {} function f(t, v) -> w, z {}
} }
// ==== // ====
// step: ssaTransform // step: ssaTransform
// ---- // ----
// { // {
// let x_2, y_3 := f(1, 2) // let x_1, y_2 := f(1, 2)
// let x := x_2 // let x := x_1
// let y := y_3 // let y := y_2
// let x_4 := mload(y_3) // let x_3 := mload(y_2)
// x := x_4 // x := x_3
// let y_5 := mload(x_4) // let y_4 := mload(x_3)
// y := y_5 // y := y_4
// let a, b := f(x_4, y_5) // let a, b := f(x_3, y_4)
// sstore(a, b) // sstore(a, b)
// function f(t, v) -> x_1, y_2 // function f(t, v) -> w, z
// { // {
// } // }
// } // }