Browse Source

Virtual functions are available for calls (#1315)

Cyrill Leutwiler 2 years ago
parent
commit
02efd3174a

+ 10 - 6
src/sema/expression/function_call.rs

@@ -164,8 +164,10 @@ pub(super) fn call_function_type(
     }
 }
 
-/// Create a list of functions that can be called in this context. If global is true, then
-/// include functions outside of contracts
+/// Create a list of functions matching the given `name` that can be called in this context.
+/// A function is available if it has a body block or if its virtual and its contract is not concrete.
+///
+/// If global is true, then include functions outside of contracts.
 pub fn available_functions(
     name: &str,
     global: bool,
@@ -188,12 +190,14 @@ pub fn available_functions(
             ns.contracts[contract_no]
                 .all_functions
                 .keys()
+                .filter(|func_no| ns.functions[**func_no].name == name)
                 .filter_map(|func_no| {
-                    if ns.functions[*func_no].name == name && ns.functions[*func_no].has_body {
-                        Some(*func_no)
-                    } else {
-                        None
+                    let is_abstract = ns.functions[*func_no].is_virtual
+                        && !ns.contracts[contract_no].is_concrete();
+                    if ns.functions[*func_no].has_body || is_abstract {
+                        return Some(*func_no);
                     }
+                    None
                 }),
         );
     }

+ 1 - 2
tests/contract.rs

@@ -164,7 +164,6 @@ fn check_diagnostics(path: &Path, ns: &Namespace) {
     }
 
     let mut found = String::new();
-
     let file = File::open(path).unwrap();
 
     for line in BufReader::new(file).lines() {
@@ -180,5 +179,5 @@ fn check_diagnostics(path: &Path, ns: &Namespace) {
 
     assert!(!found.is_empty());
 
-    assert_eq!(found, expected);
+    assert_eq!(found, expected, "source: {}", path.display());
 }

+ 17 - 0
tests/contract_testcases/substrate/functions/virtual_function_call_01.sol

@@ -0,0 +1,17 @@
+abstract contract A {
+    constructor() {
+        _virtual();
+    }
+
+    function _virtual() internal virtual;
+}
+
+contract B is A {
+    function _virtual() internal pure override {}
+
+    function m() public pure {
+        _virtual();
+    }
+}
+
+// ---- Expect: diagnostics ----

+ 19 - 0
tests/contract_testcases/substrate/functions/virtual_function_call_02.sol

@@ -0,0 +1,19 @@
+interface I {
+    function total() external returns (int256);
+}
+
+contract A {
+    function total() external returns (int256) {
+        return 4;
+    }
+}
+
+contract B is I, A {
+    function x() public {
+        int256 total = total();
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 13:24-31: functions declared external cannot be called via an internal function call
+// 	note 6:5-47: declaration of function 'total'

+ 1 - 1
tests/evm.rs

@@ -248,7 +248,7 @@ fn ethereum_solidity_tests() {
         })
         .sum();
 
-    assert_eq!(errors, 1072);
+    assert_eq!(errors, 1068);
 }
 
 fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec<String>) {