Browse Source

Ensure Expression::GetRef() is used for getting pointer to address only

Expression::GetRef() is used in single, specific setting. The
AccountMeta structure on Solana wants a pointer to an address, not
an address itself. So, Expression::GetRef() get the address of an value.

	AccountMeta({pubkey: x, is_writable: false, is_signer: false})

This code executing in other cases and is causing infinite loops, see
issue #731.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 3 years ago
parent
commit
74c6c46475

+ 10 - 6
src/sema/expression.rs

@@ -153,12 +153,6 @@ impl Expression {
             return Ok(self.clone());
         }
 
-        // First of all, if we need a ref then derefence it
-        if matches!(to, Type::Ref(..)) && !matches!(from, Type::Ref(..)) {
-            return Expression::GetRef(*loc, Type::Ref(Box::new(from)), Box::new(self.clone()))
-                .cast(loc, to, implicit, ns, diagnostics);
-        }
-
         // First of all, if we have a ref then derefence it
         if let Type::Ref(r) = from {
             return if r.is_fixed_reference_type() {
@@ -384,6 +378,16 @@ impl Expression {
 
         #[allow(clippy::comparison_chain)]
         match (&from, &to) {
+            // Solana builtin AccountMeta struct wants a pointer to an address for the pubkey field,
+            // not an address. For this specific field we have a special Expression::GetRef() which
+            // gets the pointer to an address
+            (Type::Address(_), Type::Ref(to)) if matches!(to.as_ref(), Type::Address(..)) => {
+                Ok(Expression::GetRef(
+                    *loc,
+                    Type::Ref(Box::new(from.clone())),
+                    Box::new(self.clone()),
+                ))
+            }
             (Type::Uint(from_width), Type::Enum(enum_no))
             | (Type::Int(from_width), Type::Enum(enum_no)) => {
                 if implicit {

+ 10 - 0
tests/contract_testcases/solana/address_cast.dot

@@ -0,0 +1,10 @@
+strict digraph "tests/contract_testcases/solana/address_cast.sol" {
+	contract [label="contract c\ntests/contract_testcases/solana/address_cast.sol:1:1-12"]
+	foo [label="function foo\ncontract: c\ntests/contract_testcases/solana/address_cast.sol:2:2-23\nsignature foo()\nvisibility public\nmutability nonpayable"]
+	diagnostic [label="found contract ‘c’\nlevel Debug\ntests/contract_testcases/solana/address_cast.sol:1:1-12"]
+	diagnostic_6 [label="expression is not assignable\nlevel Error\ntests/contract_testcases/solana/address_cast.sol:6:3-13"]
+	contracts -> contract
+	contract -> foo [label="function"]
+	diagnostics -> diagnostic [label="Debug"]
+	diagnostics -> diagnostic_6 [label="Error"]
+}

+ 8 - 0
tests/contract_testcases/solana/address_cast.sol

@@ -0,0 +1,8 @@
+contract c {
+	function foo() public {
+		// We have code that cast address type to ref address
+		// in fn sema::cast(). Ensure that this does not cause
+		// address values to be assignable.
+		address(0) = msg.sender;
+	}
+}