diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 047f79002..e2b79205b 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1738,6 +1738,7 @@ pub enum Builtin { BlockNumber, Calldata, ChainId, + ContractCode, Gasleft, GasLimit, Gasprice, @@ -1834,6 +1835,7 @@ impl From<&ast::Builtin> for Builtin { ast::Builtin::ChainId => Builtin::ChainId, ast::Builtin::BaseFee => Builtin::BaseFee, ast::Builtin::PrevRandao => Builtin::PrevRandao, + ast::Builtin::ContractCode => Builtin::ContractCode, _ => panic!("Builtin should not be in the cfg"), } } diff --git a/src/sema/ast.rs b/src/sema/ast.rs index 3bb0e4427..48f230bab 100644 --- a/src/sema/ast.rs +++ b/src/sema/ast.rs @@ -1586,6 +1586,7 @@ pub enum StringLocation { #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum Builtin { + ContractCode, GetAddress, Balance, PayableSend, diff --git a/src/sema/expression/member_access.rs b/src/sema/expression/member_access.rs index a761aaa99..31f880a11 100644 --- a/src/sema/expression/member_access.rs +++ b/src/sema/expression/member_access.rs @@ -347,37 +347,52 @@ pub(super) fn member_access( } _ => {} }, - Type::Address(_) => { - if id.name == "balance" { - if ns.target.is_polkadot() { - let mut is_this = false; - - if let Expression::Cast { expr: this, .. } = &expr { - if let Expression::Builtin { - kind: Builtin::GetAddress, - .. - } = this.as_ref() - { - is_this = true; - } + Type::Address(_) if id.name == "balance" => { + if ns.target.is_polkadot() { + let mut is_this = false; + + if let Expression::Cast { expr: this, .. } = &expr { + if let Expression::Builtin { + kind: Builtin::GetAddress, + .. + } = this.as_ref() + { + is_this = true; } + } - if !is_this { - diagnostics.push(Diagnostic::error( - expr.loc(), - "polkadot can only retrieve balance of this, like 'address(this).balance'".to_string(), - )); - return Err(()); - } + if !is_this { + diagnostics.push(Diagnostic::error( + expr.loc(), + "polkadot can only retrieve balance of 'this', like 'address(this).balance'" + .to_string(), + )); + return Err(()); } - used_variable(ns, &expr, symtable); - return Ok(Expression::Builtin { - loc: *loc, - tys: vec![Type::Value], - kind: Builtin::Balance, - args: vec![expr], - }); } + used_variable(ns, &expr, symtable); + return Ok(Expression::Builtin { + loc: *loc, + tys: vec![Type::Value], + kind: Builtin::Balance, + args: vec![expr], + }); + } + Type::Address(_) if id.name == "code" => { + if ns.target != Target::EVM { + diagnostics.push(Diagnostic::error( + expr.loc(), + format!("'address.code' is not supported on {}", ns.target), + )); + return Err(()); + } + used_variable(ns, &expr, symtable); + return Ok(Expression::Builtin { + loc: *loc, + tys: vec![Type::DynamicBytes], + kind: Builtin::ContractCode, + args: vec![expr], + }); } Type::Contract(ref_contract_no) => { let mut name_matches = 0; diff --git a/src/sema/mutability.rs b/src/sema/mutability.rs index 88138877e..37e2955c4 100644 --- a/src/sema/mutability.rs +++ b/src/sema/mutability.rs @@ -412,7 +412,8 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool { | Builtin::GasLimit | Builtin::MinimumBalance | Builtin::Balance - | Builtin::Accounts, + | Builtin::Accounts + | Builtin::ContractCode, .. } => state.read(loc), diff --git a/tests/contract_testcases/evm/builtins/address_code_01.sol b/tests/contract_testcases/evm/builtins/address_code_01.sol new file mode 100644 index 000000000..81808d5d0 --- /dev/null +++ b/tests/contract_testcases/evm/builtins/address_code_01.sol @@ -0,0 +1,10 @@ +contract UpgradeableProxy { + function _setImplementation( + address newImplementation + ) public pure returns (uint) { + return newImplementation.code; + } +} + +// ---- Expect: diagnostics ---- +// error: 5:9-38: conversion from bytes to uint256 not possible diff --git a/tests/contract_testcases/evm/builtins/address_code_02.sol b/tests/contract_testcases/evm/builtins/address_code_02.sol new file mode 100644 index 000000000..c04ece516 --- /dev/null +++ b/tests/contract_testcases/evm/builtins/address_code_02.sol @@ -0,0 +1,10 @@ +contract UpgradeableProxy { + function _setImplementation( + address newImplementation + ) public pure returns (bytes memory) { + return newImplementation.code; + } +} + +// ---- Expect: diagnostics ---- +// error: 5:16-38: function declared 'pure' but this expression reads from state diff --git a/tests/contract_testcases/evm/builtins/address_code_03.sol b/tests/contract_testcases/evm/builtins/address_code_03.sol new file mode 100644 index 000000000..a0713de44 --- /dev/null +++ b/tests/contract_testcases/evm/builtins/address_code_03.sol @@ -0,0 +1,9 @@ +contract UpgradeableProxy { + function _setImplementation( + address newImplementation + ) public view returns (bytes memory) { + return newImplementation.code; + } +} + +// ---- Expect: diagnostics ---- diff --git a/tests/contract_testcases/polkadot/builtins/address_code_01.sol b/tests/contract_testcases/polkadot/builtins/address_code_01.sol new file mode 100644 index 000000000..e4cd841c6 --- /dev/null +++ b/tests/contract_testcases/polkadot/builtins/address_code_01.sol @@ -0,0 +1,10 @@ +contract UpgradeableProxy { + function _setImplementation( + address newImplementation + ) public pure returns (bytes) { + return newImplementation.code; + } +} + +// ---- Expect: diagnostics ---- +// error: 5:16-33: 'address.code' is not supported on Polkadot diff --git a/tests/contract_testcases/polkadot/builtins/address_code_02.sol b/tests/contract_testcases/polkadot/builtins/address_code_02.sol new file mode 100644 index 000000000..f55f720ad --- /dev/null +++ b/tests/contract_testcases/polkadot/builtins/address_code_02.sol @@ -0,0 +1,8 @@ +contract UpgradeableProxy { + function _setImplementation(address newImplementation) public view { + assert(newImplementation.code.length != 0); + } +} + +// ---- Expect: diagnostics ---- +// error: 3:16-33: 'address.code' is not supported on Polkadot diff --git a/tests/contract_testcases/polkadot/value/balance.sol b/tests/contract_testcases/polkadot/value/balance.sol index 56384cdab..ce30b64e0 100644 --- a/tests/contract_testcases/polkadot/value/balance.sol +++ b/tests/contract_testcases/polkadot/value/balance.sol @@ -5,4 +5,4 @@ } } // ---- Expect: diagnostics ---- -// error: 4:24-25: polkadot can only retrieve balance of this, like 'address(this).balance' +// error: 4:24-25: polkadot can only retrieve balance of 'this', like 'address(this).balance' diff --git a/tests/contract_testcases/polkadot/value/balance_02.sol b/tests/contract_testcases/polkadot/value/balance_02.sol index 9955b2b0c..a23d67e74 100644 --- a/tests/contract_testcases/polkadot/value/balance_02.sol +++ b/tests/contract_testcases/polkadot/value/balance_02.sol @@ -5,4 +5,4 @@ } } // ---- Expect: diagnostics ---- -// error: 4:24-25: polkadot can only retrieve balance of this, like 'address(this).balance' +// error: 4:24-25: polkadot can only retrieve balance of 'this', like 'address(this).balance' diff --git a/tests/evm.rs b/tests/evm.rs index ca362af09..fa3aaf9b1 100644 --- a/tests/evm.rs +++ b/tests/evm.rs @@ -249,7 +249,7 @@ fn ethereum_solidity_tests() { }) .sum(); - assert_eq!(errors, 1012); + assert_eq!(errors, 1006); } fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec) {