Browse Source

Support `address.code` in sema (#1549)

Cyrill Leutwiler 2 years ago
parent
commit
d91fe95cd7

+ 2 - 0
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"),
         }
     }

+ 1 - 0
src/sema/ast.rs

@@ -1586,6 +1586,7 @@ pub enum StringLocation<T> {
 
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
 pub enum Builtin {
+    ContractCode,
     GetAddress,
     Balance,
     PayableSend,

+ 42 - 27
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;

+ 2 - 1
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),
 

+ 10 - 0
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

+ 10 - 0
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

+ 9 - 0
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 ----

+ 10 - 0
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

+ 8 - 0
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

+ 1 - 1
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'

+ 1 - 1
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'

+ 1 - 1
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<String>) {