فهرست منبع

New syntax for contracts on Solana (#1517)

Now that we represent contracts by their program id on Solana, we
decided to elaborate a new syntax to handle them. Contracts cannot be a
type in Solidity, consequently, they cannot be function arguments,
function returns nor variables.

---------

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>
Lucas Steuernagel 2 سال پیش
والد
کامیت
512f3f2387
94فایلهای تغییر یافته به همراه2046 افزوده شده و 1015 حذف شده
  1. 3 0
      docs/conf.py
  2. 2 0
      docs/examples/base_contract_function_call.sol
  3. 0 0
      docs/examples/polkadot/expression_this.sol
  4. 0 0
      docs/examples/polkadot/expression_this_external_call.sol
  5. 0 0
      docs/examples/polkadot/function_call.sol
  6. 0 0
      docs/examples/polkadot/function_call_external.sol
  7. 0 0
      docs/examples/polkadot/function_type_callback.sol
  8. 2 2
      docs/examples/solana/bobcat.sol
  9. 2 3
      docs/examples/solana/contract_address.sol
  10. 21 0
      docs/examples/solana/contract_call.sol
  11. 21 0
      docs/examples/solana/contract_new.sol
  12. 2 5
      docs/examples/solana/create_contract_with_metas.sol
  13. 6 0
      docs/examples/solana/expression_this.sol
  14. 10 0
      docs/examples/solana/expression_this_external_call.sol
  15. 27 0
      docs/examples/solana/function_call.sol
  16. 16 0
      docs/examples/solana/function_call_external.sol
  17. 24 0
      docs/examples/solana/function_type_callback.sol
  18. 2 3
      docs/examples/solana/payer_annotation.sol
  19. 13 4
      docs/examples/solana/program_id.sol
  20. 42 9
      docs/language/contracts.rst
  21. 24 4
      docs/language/expressions.rst
  22. 30 7
      docs/language/functions.rst
  23. 21 2
      docs/language/types.rst
  24. 1 0
      docs/requirements.txt
  25. 5 3
      docs/targets/solana.rst
  26. 5 5
      integration/anchor/tests/call_anchor.spec.ts
  27. 9 11
      integration/solana/create_contract.sol
  28. 10 10
      integration/solana/external_call.sol
  29. 2 2
      integration/solana/runtime_errors.sol
  30. 4 0
      solang-parser/src/solidity.lalrpop
  31. 1 1
      src/abi/anchor.rs
  32. 54 36
      src/abi/tests.rs
  33. 4 9
      src/bin/idl/mod.rs
  34. 18 8
      src/codegen/constructor.rs
  35. 1 1
      src/codegen/mod.rs
  36. 33 19
      src/codegen/solana_accounts/account_collection.rs
  37. 25 0
      src/codegen/solana_accounts/account_management.rs
  38. 15 7
      src/codegen/statements/mod.rs
  39. 4 9
      src/emit/solana/target.rs
  40. 8 4
      src/sema/ast.rs
  41. 3 2
      src/sema/builtin.rs
  42. 1 1
      src/sema/contracts.rs
  43. 67 20
      src/sema/expression/constructor.rs
  44. 721 354
      src/sema/expression/function_call.rs
  45. 2 1
      src/sema/expression/member_access.rs
  46. 15 2
      src/sema/functions.rs
  47. 43 11
      src/sema/namespace.rs
  48. 8 2
      src/sema/statements.rs
  49. 8 12
      src/sema/tests/mod.rs
  50. 29 6
      src/sema/types.rs
  51. 3 0
      src/sema/unused_variable.rs
  52. 8 1
      src/sema/using.rs
  53. 8 1
      src/sema/variables.rs
  54. 6 0
      tests/codegen_testcases/import_test.sol
  55. 16 0
      tests/codegen_testcases/solidity/accounts_for_default_constructor.sol
  56. 98 101
      tests/codegen_testcases/solidity/borsh_decoding_simple_types.sol
  57. 3 4
      tests/codegen_testcases/solidity/constructor_with_metas.sol
  58. 25 0
      tests/codegen_testcases/solidity/import_ext_call.sol
  59. 46 0
      tests/codegen_testcases/solidity/solana_base_versus_external.sol
  60. 3 4
      tests/codegen_testcases/solidity/solana_payer_account.sol
  61. 14 0
      tests/contract_testcases/polkadot/contracts/program_id.sol
  62. 2 2
      tests/contract_testcases/solana/abstract_interface.sol
  63. 16 18
      tests/contract_testcases/solana/accounts/constructor_in_loop.sol
  64. 14 17
      tests/contract_testcases/solana/accounts/double_calls.sol
  65. 3 5
      tests/contract_testcases/solana/annotations/account_name_collision.sol
  66. 6 8
      tests/contract_testcases/solana/annotations/constructor_external_function.sol
  67. 9 9
      tests/contract_testcases/solana/call/call_args_three_ways.sol
  68. 1 2
      tests/contract_testcases/solana/constant/not_constant.sol
  69. 23 0
      tests/contract_testcases/solana/contracts/abstract_constructor.sol
  70. 48 0
      tests/contract_testcases/solana/contracts/circular_reference.sol
  71. 29 0
      tests/contract_testcases/solana/contracts/constructor_freestanding.sol
  72. 51 0
      tests/contract_testcases/solana/contracts/contract_as_variables.sol
  73. 28 0
      tests/contract_testcases/solana/contracts/contract_call_freestanding.sol
  74. 2 2
      tests/contract_testcases/solana/create_contract/syntax.sol
  75. 2 2
      tests/contract_testcases/solana/create_contract/syntax_01.sol
  76. 1 1
      tests/contract_testcases/solana/destructure_assign_struct_member_2.sol
  77. 2 3
      tests/contract_testcases/solana/error.sol
  78. 0 19
      tests/contract_testcases/solana/expressions/contract_compare.sol
  79. 3 4
      tests/contract_testcases/solana/expressions/contract_no_init.sol
  80. 3 3
      tests/contract_testcases/solana/expressions/selector_in_free_function_02.sol
  81. 3 2
      tests/contract_testcases/solana/functions/external_functions.sol
  82. 6 5
      tests/contract_testcases/solana/garbage_function_args.sol
  83. 5 5
      tests/contract_testcases/solana/mapping_deletion.sol
  84. 2 2
      tests/contract_testcases/solana/type_decl_import.sol
  85. 0 9
      tests/optimization_testcases/calls/54e619457eea3be2790d0fa66cba06d2e9a36f17.json
  86. 0 21
      tests/optimization_testcases/programs/54e619457eea3be2790d0fa66cba06d2e9a36f17.sol
  87. 2 2
      tests/solana_tests/abi_decode.rs
  88. 3 2
      tests/solana_tests/abi_encode.rs
  89. 8 5
      tests/solana_tests/accessor.rs
  90. 11 12
      tests/solana_tests/call.rs
  91. 30 52
      tests/solana_tests/create_contract.rs
  92. 5 7
      tests/solana_tests/mappings.rs
  93. 15 18
      tests/solana_tests/runtime_errors.rs
  94. 119 92
      tests/solana_tests/using.rs

+ 3 - 0
docs/conf.py

@@ -39,8 +39,11 @@ release = os.popen('git describe --tags').readline().strip()
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
+    'sphinx_tabs.tabs'
 ]
 
+# Do not allow tabs to be closed
+sphinx_tabs_disable_tab_closing = True
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 

+ 2 - 0
docs/examples/base_contract_function_call.sol

@@ -22,6 +22,8 @@ abstract contract a {
 
     function bar2() internal returns (uint64) {
         // this explicitly says "call foo of base contract a", and dispatch is not virtual
+        // however, if the call is written as a.foo{program_id: id_var}(), this represents
+        // an external call to contract 'a' on Solana.
         return a.foo();
     }
 }

+ 0 - 0
docs/examples/expression_this.sol → docs/examples/polkadot/expression_this.sol


+ 0 - 0
docs/examples/expression_this_external_call.sol → docs/examples/polkadot/expression_this_external_call.sol


+ 0 - 0
docs/examples/function_call.sol → docs/examples/polkadot/function_call.sol


+ 0 - 0
docs/examples/function_call_external.sol → docs/examples/polkadot/function_call_external.sol


+ 0 - 0
docs/examples/function_type_callback.sol → docs/examples/polkadot/function_type_callback.sol


+ 2 - 2
docs/examples/solana/bobcat.sol

@@ -1,5 +1,5 @@
-anchor_anchor constant bobcat = anchor_anchor(address'z7FbDfQDfucxJz5o8jrGLgvSbdoeSqX5VrxBb5TVjHq');
-interface anchor_anchor {
+@program_id("z7FbDfQDfucxJz5o8jrGLgvSbdoeSqX5VrxBb5TVjHq")
+interface bobcat {
 	@selector([0xaf, 0xaf, 0x6d, 0x1f, 0x0d, 0x98, 0x9b, 0xed])
 	function pounce() view external returns(int64);
 }

+ 2 - 3
docs/examples/solana/contract_address.sol

@@ -1,4 +1,3 @@
-@program_id("5kQ3iJ43gHNDjqmSAtE1vDu18CiSAfNbRe4v5uoobh3U")
 contract hatchling {
     string name;
 
@@ -9,7 +8,7 @@ contract hatchling {
 }
 
 contract adult {
-    function test(address addr) external {
-        hatchling h = new hatchling("luna");
+    function test(address id) external {
+        hatchling.new{program_id: id}("luna");
     }
 }

+ 21 - 0
docs/examples/solana/contract_call.sol

@@ -0,0 +1,21 @@
+contract Polymath {
+    function call_math() external returns (uint) {
+        return Math.sum(1, 2);
+    }
+    function call_english(address english_id) external returns (string) {
+        return English.concatenate{program_id: english_id}("Hello", "world");
+    }
+}
+
+@program_id("5afzkvPkrshqu4onwBCsJccb1swrt4JdAjnpzK8N4BzZ")
+contract Math {
+    function sum(uint a, uint b) external returns (uint) {
+        return a + b;
+    }
+}
+
+contract English {
+    function concatenate(string a, string b) external returns (string) {
+        return a + b;
+    }
+}

+ 21 - 0
docs/examples/solana/contract_new.sol

@@ -0,0 +1,21 @@
+@program_id("5afzkvPkrshqu4onwBCsJccb1swrt4JdAjnpzK8N4BzZ")
+contract hatchling {
+    string name;
+    address private origin;
+
+    constructor(string id, address parent) {
+        require(id != "", "name must be provided");
+        name = id;
+        origin = parent;
+    }
+
+    function root() public returns (address) {
+        return origin;
+    }
+}
+
+contract adult {
+    function test() external {
+        hatchling.new("luna", address(this));
+    }
+}

+ 2 - 5
docs/examples/solana/create_contract_with_metas.sol

@@ -1,9 +1,6 @@
 import 'solana';
 
 contract creator {
-    Child public c;
-    Child public c_metas;
-
     function create_with_metas(address data_account_to_initialize, address payer) public {
         AccountMeta[3] metas = [
             AccountMeta({
@@ -20,9 +17,9 @@ contract creator {
                 is_signer: false})
         ];
 
-        c_metas = new Child{accounts: metas}(payer);        
+        Child.new{accounts: metas}(payer);        
   
-        c_metas.use_metas();
+        Child.use_metas();
     }
 }
 

+ 6 - 0
docs/examples/solana/expression_this.sol

@@ -0,0 +1,6 @@
+contract kadowari {
+    function nomi() public {
+        // Contracts are not allowed as variables on Solana
+        address a = address(this);
+    }
+}

+ 10 - 0
docs/examples/solana/expression_this_external_call.sol

@@ -0,0 +1,10 @@
+@program_id("H3AthiA2C1pcMahg17nEwqr9628gkXUnnzWJJ3iSDekL")
+contract kadowari {
+    function nomi() public {
+        this.nokogiri(102);
+    }
+
+    function nokogiri(int256 a) public {
+        // ...
+    }
+}

+ 27 - 0
docs/examples/solana/function_call.sol

@@ -0,0 +1,27 @@
+contract A {
+    function test(address v) public {
+        // the following four lines are equivalent to "uint32 res = v.foo(3,5);"
+
+        // Note that the signature is only hashed and not parsed. So, ensure that the
+        // arguments are of the correct type.
+        bytes data = abi.encodeWithSignature(
+            "global:foo",
+            uint32(3),
+            uint32(5)
+        );
+
+        (bool success, bytes rawresult) = v.call(data);
+
+        assert(success == true);
+
+        uint32 res = abi.decode(rawresult, (uint32));
+
+        assert(res == 8);
+    }
+}
+
+contract B {
+    function foo(uint32 a, uint32 b) pure public returns (uint32) {
+        return a + b;
+    }
+}

+ 16 - 0
docs/examples/solana/function_call_external.sol

@@ -0,0 +1,16 @@
+contract foo {
+    function bar1(uint32 x, bool y) public returns (address, bytes32) {
+        return (address(3), hex"01020304");
+    }
+
+    function bar2(uint32 x, bool y) public returns (bool) {
+        return !y;
+    }
+}
+
+contract bar {
+    function test(address f) public {
+        (address f1, bytes32 f2) = foo.bar1{program_id: f}(102, false);
+        bool f3 = foo.bar2{program_id: f}({x: 255, y: true});
+    }
+}

+ 24 - 0
docs/examples/solana/function_type_callback.sol

@@ -0,0 +1,24 @@
+contract ft {
+    function test(address p) public {
+        // this.callback can be used as an external function type value
+        paffling.set_callback{program_id: p}(this.callback);
+    }
+
+    function callback(int32 count, string foo) public {
+        // ...
+    }
+}
+
+contract paffling {
+    // the first visibility "external" is for the function type, the second "internal" is
+    // for the callback variables
+    function(int32, string) external internal callback;
+
+    function set_callback(function(int32, string) external c) public {
+        callback = c;
+    }
+
+    function piffle() public {
+        callback(1, "paffled");
+    }
+}

+ 2 - 3
docs/examples/solana/payer_annotation.sol

@@ -2,11 +2,10 @@ import 'solana';
 
 @program_id("SoLDxXQ9GMoa15i4NavZc61XGkas2aom4aNiWT6KUER")
 contract Builder {
-    BeingBuilt other;
     function build_this() external {
         // When calling a constructor from an external function, the data account for the contract
         // 'BeingBuilt' should be passed as the 'BeingBuilt_dataAccount' in the client code.
-        other = new BeingBuilt("my_seed");
+        BeingBuilt.new("my_seed");
     }
 
     function build_that(address data_account, address payer_account) public {
@@ -30,7 +29,7 @@ contract Builder {
                 is_signer: false
                 })
         ];
-        other = new BeingBuilt{accounts: metas}("my_seed");
+        BeingBuilt.new{accounts: metas}("my_seed");
     }
 }
 

+ 13 - 4
docs/examples/solana/program_id.sol

@@ -5,14 +5,23 @@ contract Foo {
     }
 }
 
-contract Bar {
-    Foo public foo;
+contract OtherFoo {
+    function say_bye() public pure {
+        print("Bye from other foo");
+    }
+}
 
+contract Bar {
     function create_foo() external {
-        foo = new Foo();
+        Foo.new();
     }
 
     function call_foo() public {
-        foo.say_hello();
+        Foo.say_hello();
+    }
+
+    function foo_at_another_address(address other_foo_id) external {
+        OtherFoo.new{program_id: other_foo_id}();
+        OtherFoo.say_bye{program_id: other_foo_id}();
     }
 }

+ 42 - 9
docs/language/contracts.rst

@@ -31,10 +31,24 @@ Instantiation using new
 _______________________
 
 Contracts can be created using the ``new`` keyword. The contract that is being created might have
-constructor arguments, which need to be provided.
+constructor arguments, which need to be provided. While on Polkadot and Ethereum constructors return the address
+of the instantiated contract, on Solana, the address is either passed to the call using the ``{program_id: ...}`` call
+argument or is declared above a contract with the ``@program_id`` annotation. As the constructor does not return
+anything and its purpose is only to initialize the data account, the syntax ``new Contract()``is not idiomatic on Solana.
+Instead, a function ``new`` is made available to call the constructor.
+
+.. tabs::
+
+    .. group-tab:: Polkadot
+
+        .. include:: ../examples/polkadot/contract_new.sol
+            :code: solidity
 
-.. include:: ../examples/polkadot/contract_new.sol
-  :code: solidity
+
+    .. group-tab:: Solana
+
+        .. include:: ../examples/solana/contract_new.sol
+            :code: solidity
 
 The constructor might fail for various reasons, for example ``require()`` might fail here. This can
 be handled using the :ref:`try-catch` statement, else errors cause the transaction to fail.
@@ -87,15 +101,16 @@ can use. gas is a ``uint64``.
 .. include:: ../examples/polkadot/contract_gas_limit.sol
   :code: solidity
 
+
 .. _solana_constructor:
 
-Instantiating a contract on Solana
-__________________________________
+Solana constructors
+___________________
 
-On Solana, the contract being created must have the ``@program_id()`` annotation that specifies the program account to
-which the contract code has been deployed. This account holds only the contract's executable binary.
-When calling a constructor only once from an external function, no call arguments are needed. The data account
-necessary to initialize the contract should be present in the IDL and is identified as ``contractName_dataAccount``.
+Solidity contracts are coupled to a data account, which stores the contract's state variables on the blockchain.
+This account must be initialized before calling other contract functions, if they require one. A contract constructor
+initializes the data account and can be called with the ``new`` function. When invoking the constructor from another
+contract, the data account to initialize appears in the IDL file and is identified as ``contractName_dataAccount``.
 In the example below, the IDL for the instruction ``test`` requires the ``hatchling_dataAccount`` account to be
 initialized as the new contract's data account.
 
@@ -125,6 +140,24 @@ The sequence of the accounts in the ``AccountMeta`` array matters and must follo
 :ref:`IDL ordering <account_management>`.
 
 
+.. _solana_contract_call:
+
+Calling a contract on Solana
+____________________________
+
+A call to a contract on Solana follows a different syntax than that of Solidity on Ethereum or Polkadot. As contracts
+cannot be a variable, calling a contract's function follows the syntax ``Contract.function()``. If the contract
+definition contains the ``@program_id`` annotation, the CPI will be directed to the address declared inside the
+annotation.
+
+If that annotation is not present, the program address must be manually specified with the ``{program_id: ... }`` call
+argument. When both the annotation and the call argument are present, the compiler will forward the call to the address
+specified in the call argument.
+
+.. include:: ../examples/solana/contract_call.sol
+  :code: solidity
+
+
 Base contracts, abstract contracts and interfaces
 -------------------------------------------------
 

+ 24 - 4
docs/language/expressions.rst

@@ -112,15 +112,35 @@ ____
 The keyword ``this`` evaluates to the current contract. The type of this is the type of the
 current contract. It can be cast to ``address`` or ``address payable`` using a cast.
 
-.. include:: ../examples/expression_this.sol
-  :code: solidity
+.. tabs::
+
+    .. group-tab:: Polkadot
+
+        .. include:: ../examples/polkadot/expression_this.sol
+            :code: solidity
+
+
+    .. group-tab:: Solana
+
+        .. include:: ../examples/solana/expression_this.sol
+            :code: solidity
 
 Function calls made via this are function calls through the external call mechanism; i.e. they
 have to serialize and deserialise the arguments and have the external call overhead. In addition,
 this only works with public functions.
 
-.. include:: ../examples/expression_this_external_call.sol
-  :code: solidity
+.. tabs::
+
+    .. group-tab:: Polkadot
+
+        .. include:: ../examples/polkadot/expression_this_external_call.sol
+            :code: solidity
+
+
+    .. group-tab:: Solana
+
+        .. include:: ../examples/solana/expression_this_external_call.sol
+            :code: solidity
 
 .. note::
 

+ 30 - 7
docs/language/functions.rst

@@ -66,12 +66,25 @@ external functions. The called function must be declared public.
 Calling external functions requires ABI encoding the arguments, and ABI decoding the
 return values. This much more costly than an internal function call.
 
-.. include:: ../examples/function_call_external.sol
-  :code: solidity
 
-The syntax for calling external call is the same as the external call, except for
-that it must be done on a contract type variable. Any error in an external call can
-be handled with :ref:`try-catch`.
+.. tabs::
+
+    .. group-tab:: Polkadot
+
+        .. include:: ../examples/polkadot/function_call_external.sol
+            :code: solidity
+
+
+    .. group-tab:: Solana
+
+        .. include:: ../examples/solana/function_call_external.sol
+            :code: solidity
+
+
+
+The syntax for calling a contract is the same as that of the external call, except
+that it must be done on a contract type variable. Errors in external calls can
+be handled with :ref:`try-catch` only on Polkadot.
 
 Internal calls and externals calls
 ___________________________________
@@ -289,8 +302,18 @@ This takes a single argument, which should be the ABI encoded arguments. The ret
 values are a ``boolean`` which indicates success if true, and the ABI encoded
 return value in ``bytes``.
 
-.. include:: ../examples/function_call.sol
-  :code: solidity
+.. tabs::
+
+    .. group-tab:: Polkadot
+
+        .. include:: ../examples/polkadot/function_call.sol
+            :code: solidity
+
+
+    .. group-tab:: Solana
+
+        .. include:: ../examples/solana/function_call.sol
+            :code: solidity
 
 Any value or gas limit can be specified for the external call. Note that no check is done to see
 if the called function is ``payable``, since the compiler does not know what function you are

+ 21 - 2
docs/language/types.rst

@@ -460,6 +460,14 @@ The expression ``this`` evaluates to the current contract, which can be cast to
 .. include:: ../examples/contract_type_cast_address.sol
   :code: solidity
 
+.. _contracts_not_types:
+
+.. note::
+    On Solana, contracts cannot exist as types, so contracts cannot be function parameters, function returns
+    or variables. Contracts on Solana are deployed to a defined address, which is often known during compile time,
+    so there is no need to hold that address as a variable underneath a contract type.
+
+
 Function Types
 ______________
 
@@ -485,8 +493,19 @@ the contract, and the function selector. An internal function type only stores t
 assigning a value to an external function selector, the contract and function must be specified, by using
 a function on particular contract instance.
 
-.. include:: ../examples/function_type_callback.sol
-  :code: solidity
+.. tabs::
+
+    .. group-tab:: Polkadot
+
+        .. include:: ../examples/polkadot/function_type_callback.sol
+            :code: solidity
+
+
+    .. group-tab:: Solana
+
+        .. include:: ../examples/solana/function_type_callback.sol
+            :code: solidity
+
 
 Storage References
 __________________

+ 1 - 0
docs/requirements.txt

@@ -3,4 +3,5 @@ sphinx_rtd_theme>=0.5.2
 pygments-lexer-solidity>=0.7.0
 sphinx-a4doc>=1.2.1
 sphinx>=2.1.0
+sphinx-tabs>=3.4.1
 

+ 5 - 3
docs/targets/solana.rst

@@ -46,9 +46,11 @@ Runtime
 - The Solana target requires `Solana <https://www.solana.com/>`_ v1.8.1.
 - Function selectors are eight bytes wide and known as *discriminators*.
 - Solana provides different builtins, e.g. ``block.slot`` and ``tx.accounts``.
-- When calling an external function or instantiating a contract using ``new``, one
+- When calling an external function or invoking a contract's constructor, one
   :ref:`needs to provide <solana_constructor>` the necessary accounts for the transaction.
 - The keyword ``this`` returns the contract's program account, also know as program id.
+- Contracts :ref:`cannot be types <contracts_not_types>` on Solana and :ref:`calls to contracts <solana_contract_call>`
+  follow a different syntax.
 
 
 Compute budget
@@ -127,8 +129,8 @@ _____________________________________
 
 When developing contracts for Solana, programs are usually deployed to a well
 known account. The account can be specified in the source code using an annotation
-``@program_id``. If you want to instantiate a contract using the
-``new ContractName()`` syntax, then the contract must have a program_id annotation.
+``@program_id`` if it is known beforehand. If you want to call a contract via an external call,
+either the contract must have a ``@program_id`` annotation or the ``{program_id: ..}`` call argument must be present.
 
 .. include:: ../examples/solana/program_id.sol
   :code: solidity

+ 5 - 5
integration/anchor/tests/call_anchor.spec.ts

@@ -50,11 +50,8 @@ describe('Call Anchor program from Solidity via IDL', () => {
         const ret = await program.methods.data().accounts({ dataAccount: storage.publicKey }).view();
         expect(ret).toEqual(data.publicKey);
 
+        const anchor_program_id = new PublicKey("z7FbDfQDfucxJz5o8jrGLgvSbdoeSqX5VrxBb5TVjHq");
         const remainingAccounts: AccountMeta[] = [{
-            pubkey: new PublicKey("z7FbDfQDfucxJz5o8jrGLgvSbdoeSqX5VrxBb5TVjHq"),
-            isSigner: false,
-            isWritable: false,
-        }, {
             pubkey: data.publicKey,
             isSigner: true,
             isWritable: true,
@@ -65,7 +62,10 @@ describe('Call Anchor program from Solidity via IDL', () => {
         }];
 
         await program.methods.test(payer.publicKey)
-            .accounts({ dataAccount: storage.publicKey })
+            .accounts({
+                dataAccount: storage.publicKey,
+                anchor_programId: anchor_program_id,
+            })
             .remainingAccounts(remainingAccounts)
             .signers([data, payer])
             .rpc();

+ 9 - 11
integration/solana/create_contract.sol

@@ -1,27 +1,25 @@
 import 'solana';
 
 contract creator {
-    Child public c;
-    Child public c_metas;
 
     function create_child() external {
         print("Going to create child");
-        c = new Child();
+        Child.new();
 
-        c.say_hello();
+        Child.say_hello();
     }
 
     function create_seed1(bytes seed, bytes1 bump, uint64 space) external {
         print("Going to create Seed1");
-        Seed1 s = new Seed1(seed, bump, space);
+        Seed1.new(seed, bump, space);
 
-        s.say_hello();
+        Seed1.say_hello();
     }
 
     function create_seed2(bytes seed, uint32 space) external {
         print("Going to create Seed2");
 
-        new Seed2(seed, space);
+        Seed2.new(seed, space);
     }
 
     function create_child_with_metas(address child, address payer) public {
@@ -32,13 +30,13 @@ contract creator {
             AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
         ];
 
-        c_metas = new Child{accounts: metas}();        
-        c_metas.use_metas();
+        Child.new{accounts: metas}();        
+        Child.use_metas();
     }
 
     function create_without_annotation() external {
-        MyCreature cc = new MyCreature();
-        cc.say_my_name();
+        MyCreature.new();
+        MyCreature.say_my_name();
     }
 }
 

+ 10 - 10
integration/solana/external_call.sol

@@ -1,21 +1,21 @@
 
 contract caller {
-    function do_call(callee e, int64 v) public {
-        e.set_x(v);
+    function do_call(address e, int64 v) public {
+        callee.set_x{program_id: e}(v);
     }
 
-    function do_call2(callee e, int64 v) view public returns (int64) {
-        return v + e.get_x();
+    function do_call2(address e, int64 v) view public returns (int64) {
+        return v + callee.get_x{program_id: e}();
     }
 
     // call two different functions
-    function do_call3(callee e, callee2 e2, int64[4] memory x, string memory y) pure public returns (int64, string memory) {
-        return (e2.do_stuff(x), e.get_name());
+    function do_call3(address e, address e2, int64[4] memory x, string memory y) pure public returns (int64, string memory) {
+        return (callee2.do_stuff{program_id: e2}(x), callee.get_name{program_id: e}());
     }
 
     // call two different functions
-    function do_call4(callee e, callee2 e2, int64[4] memory x, string memory y) pure public returns (int64, string memory) {
-        return (e2.do_stuff(x), e.call2(e2, y));
+    function do_call4(address e, address e2, int64[4] memory x, string memory y) pure public returns (int64, string memory) {
+        return (callee2.do_stuff{program_id: e2}(x), callee.call2{program_id: e}(e2, y));
     }
 
     function who_am_i() public view returns (address) {
@@ -34,8 +34,8 @@ contract callee {
         return x;
     }
 
-    function call2(callee2 e2, string s) public pure returns (string) {
-        return e2.do_stuff2(s);
+    function call2(address e2, string s) public pure returns (string) {
+        return callee2.do_stuff2{program_id: e2}(s);
     }
 
     function get_name() public pure returns (string) {

+ 2 - 2
integration/solana/runtime_errors.sol

@@ -61,8 +61,8 @@ contract RuntimeErrors {
     }
 
     // external call failed
-    function call_ext(Creature e) public {
-        e.say_my_name();
+    function call_ext() public {
+        Creature.say_my_name();
     }
 
     function i_will_revert() public {

+ 4 - 0
solang-parser/src/solidity.lalrpop

@@ -483,6 +483,10 @@ NoFunctionTyPrecedence0: Expression = {
         Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e),
             Identifier { loc: Loc::File(file_no, al, b), name: "address".to_string() })
     },
+    <a:@L> <e:Precedence1> "." <al:@L> "new" <b:@R> => {
+        Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e),
+            Identifier { loc: Loc::File(file_no, al, b), name: "new".to_string() })
+    },
     <l:@L> <ty:NoFunctionType> <r:@R> => Expression::Type(Loc::File(file_no, l, r), ty),
     <a:@L> "[" <v:CommaOne<Expression>> "]" <b:@R> => {
         Expression::ArrayLiteral(Loc::File(file_no, a, b), v)

+ 1 - 1
src/abi/anchor.rs

@@ -103,7 +103,7 @@ fn idl_instructions(
 ) -> Vec<IdlInstruction> {
     let mut instructions: Vec<IdlInstruction> = Vec::new();
 
-    if !contract.have_constructor(ns) {
+    if contract.constructors(ns).is_empty() {
         instructions.push(IdlInstruction {
             name: "new".to_string(),
             docs: None,

+ 54 - 36
src/abi/tests.rs

@@ -1697,30 +1697,25 @@ fn other_collected_public_keys() {
     let src = r#"
     import 'solana';
 
-anchor_anchor constant anchor = anchor_anchor(address'SysvarRent111111111111111111111111111111111');
-
-interface anchor_anchor {
+@program_id("SysvarRent111111111111111111111111111111111")
+interface anchor {
 	@selector([0xaf,0xaf,0x6d,0x1f,0x0d,0x98,0x9b,0xed])
 	function initialize(bool data1) view external;
 }
 
-associated constant ass = associated(address'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
-
+@program_id("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
 interface associated {
 	@selector([0xaf,0xaf,0x6d,0x1f,0x0d,0x98,0x9b,0xed])
 	function initialize(bool data1) view external;
 }
 
-
-clock_interface constant my_clock = clock_interface(address'SysvarC1ock11111111111111111111111111111111');
-
+@program_id("SysvarC1ock11111111111111111111111111111111")
 interface clock_interface {
 	@selector([0xaf,0xaf,0x6d,0x1f,0x0d,0x98,0x9b,0xed])
 	function initialize(bool data1) view external;
 }
 
-other_interface constant other_inter = other_interface(address'z7FbDfQDfucxJz5o8jrGLgvSbdoeSqX5VrxBb5TVjHq');
-
+@program_id("z7FbDfQDfucxJz5o8jrGLgvSbdoeSqX5VrxBb5TVjHq")
 interface other_interface {
 	@selector([0xaf,0xaf,0x6d,0x1f,0x0d,0x98,0x9b,0xed])
 	function initialize(bool data1) view external;
@@ -1732,15 +1727,15 @@ contract Test {
     }
 
     function call_2() public {
-        ass.initialize(false);
+        associated.initialize(false);
     }
 
     function call_3() public {
-        my_clock.initialize(true);
+        clock_interface.initialize(true);
     }
 
     function call_4() public {
-        other_inter.initialize(false);
+        other_interface.initialize(false);
     }
 }
     "#;
@@ -1798,13 +1793,10 @@ fn multiple_contracts() {
     import 'solana';
 
 contract creator {
-    Child public c;
-
     function create_child() external returns (uint64) {
         print("Going to create child");
-        c = new Child();
-
-        return c.say_hello();
+        Child.new();
+        return Child.say_hello();
     }
 }
 
@@ -1828,17 +1820,15 @@ contract Child {
     let idl = generate_anchor_idl(0, &ns, "0.1.0");
 
     assert_eq!(idl.instructions[0].name, "new");
-    assert_eq!(idl.instructions[1].name, "c");
-    assert_eq!(idl.instructions[2].name, "create_child");
+    assert_eq!(idl.instructions[1].name, "create_child");
 
     assert_eq!(
-        idl.instructions[2].accounts,
+        idl.instructions[1].accounts,
         vec![
-            idl_account("dataAccount", true, false),
+            idl_account("systemProgram", false, false),
+            idl_account("Child_programId", false, false),
             idl_account("payer", true, true),
             idl_account("Child_dataAccount", true, true),
-            idl_account("Child_programId", false, false),
-            idl_account("systemProgram", false, false),
             idl_account("clock", false, false),
         ]
     );
@@ -1851,11 +1841,9 @@ fn constructor_double_payer() {
 
 @program_id("SoLDxXQ9GMoa15i4NavZc61XGkas2aom4aNiWT6KUER")
 contract Builder {
-    BeingBuilt other;
-
     @payer(payer_account)
     constructor() {
-        other = new BeingBuilt("abc");
+       BeingBuilt.new("abc");
     }
 }
 
@@ -1883,9 +1871,9 @@ contract BeingBuilt {
             idl_account("dataAccount", true, true),
             idl_account("payer_account", true, true),
             idl_account("systemProgram", false, false),
+            idl_account("BeingBuilt_programId", false, false),
             idl_account("other_account", true, true),
             idl_account("BeingBuilt_dataAccount", true, false),
-            idl_account("BeingBuilt_programId", false, false),
         ]
     );
 }
@@ -1956,19 +1944,17 @@ contract starter {
 fn account_transfer_recursive() {
     let src = r#"
 contract CT3 {
-    CT2 ct2;
     @payer(three_payer)
     constructor() {
-        ct2 = new CT2();
+        CT2.new();
     }
 }
 
 @program_id("Ha2EGxARbSYpqNZkkvZUUGEyx3pu7Mg9pvMsuEJuWNjH")
 contract CT2 {
-    CT1 ct1;
     @payer(two_payer)
     constructor() {
-        ct1 = new CT1(block.timestamp);
+        CT1.new(block.timestamp);
     }
 }
 
@@ -2005,10 +1991,10 @@ contract CT1 {
             idl_account("dataAccount", true, true),
             idl_account("two_payer", true, true),
             idl_account("clock", false, false),
+            idl_account("systemProgram", false, false),
+            idl_account("CT1_programId", false, false),
             idl_account("one_payer", true, true),
             idl_account("CT1_dataAccount", true, true),
-            idl_account("CT1_programId", false, false),
-            idl_account("systemProgram", false, false),
         ]
     );
 
@@ -2019,13 +2005,45 @@ contract CT1 {
             idl_account("dataAccount", true, true),
             idl_account("three_payer", true, true),
             idl_account("systemProgram", false, false),
+            idl_account("CT2_programId", false, false),
             idl_account("two_payer", true, true),
             idl_account("CT2_dataAccount", true, true),
-            idl_account("CT2_programId", false, false),
             idl_account("clock", false, false),
+            idl_account("CT1_programId", false, false),
             idl_account("one_payer", true, true),
             idl_account("CT1_dataAccount", true, true),
-            idl_account("CT1_programId", false, false),
+        ]
+    );
+}
+
+#[test]
+fn default_constructor() {
+    let src = r#"
+contract Foo {
+    uint b;
+    function get_b() public returns (uint) {
+        return b;
+    }
+}
+
+contract Other {
+    function call_foo(address id) external {
+        Foo.new{program_id: id}();
+    }
+}
+    "#;
+
+    let mut ns = generate_namespace(src);
+    codegen(&mut ns, &Options::default());
+    let idl = generate_anchor_idl(1, &ns, "0.0.1");
+
+    assert_eq!(idl.instructions[1].name, "call_foo");
+    assert_eq!(
+        idl.instructions[1].accounts,
+        vec![
+            idl_account("Foo_dataAccount", true, false),
+            idl_account("Foo_programId", false, false),
+            idl_account("systemProgram", false, false)
         ]
     );
 }

+ 4 - 9
src/bin/idl/mod.rs

@@ -63,14 +63,6 @@ fn idl_file(file: &OsStr, output: &Option<PathBuf>) {
 }
 
 fn write_solidity(idl: &Idl, mut f: File) -> Result<(), std::io::Error> {
-    if let Some(program_id) = program_id(idl) {
-        writeln!(
-            f,
-            "anchor_{} constant {} = anchor_{}(address'{}');\n",
-            idl.name, idl.name, idl.name, program_id
-        )?;
-    }
-
     let mut ty_names = idl
         .types
         .iter()
@@ -212,7 +204,10 @@ fn write_solidity(idl: &Idl, mut f: File) -> Result<(), std::io::Error> {
 
     docs(&mut f, 0, &idl.docs)?;
 
-    writeln!(f, "interface anchor_{} {{", idl.name)?;
+    if let Some(program_id) = program_id(idl) {
+        writeln!(f, "@program_id(\"{}\")", program_id)?;
+    }
+    writeln!(f, "interface {} {{", idl.name)?;
 
     let mut instruction_names = idl
         .instructions

+ 18 - 8
src/codegen/constructor.rs

@@ -47,14 +47,24 @@ pub(super) fn call_constructor(
         .as_ref()
         .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
     let address = if ns.target == Target::Solana {
-        Some(Expression::NumberLiteral {
-            loc: Loc::Codegen,
-            ty: Type::Address(false),
-            value: BigInt::from_bytes_be(
-                Sign::Plus,
-                ns.contracts[contract_no].program_id.as_ref().unwrap(),
-            ),
-        })
+        if let Some(literal_id) = &ns.contracts[contract_no].program_id {
+            Some(Expression::NumberLiteral {
+                loc: Loc::Codegen,
+                ty: Type::Address(false),
+                value: BigInt::from_bytes_be(Sign::Plus, literal_id),
+            })
+        } else {
+            let address = expression(
+                call_args.program_id.as_ref().unwrap(),
+                cfg,
+                callee_contract_no,
+                func,
+                ns,
+                vartab,
+                opt,
+            );
+            Some(address)
+        }
     } else {
         None
     };

+ 1 - 1
src/codegen/mod.rs

@@ -234,7 +234,7 @@ fn contract(contract_no: usize, ns: &mut Namespace, opt: &Options) {
         all_cfg.push(cfg);
         ns.contracts[contract_no].initializer = Some(pos);
 
-        if !ns.contracts[contract_no].have_constructor(ns) {
+        if ns.contracts[contract_no].constructors(ns).is_empty() {
             // generate the default constructor
             let func = ns.default_constructor(contract_no);
             let cfg_no = all_cfg.len();

+ 33 - 19
src/codegen/solana_accounts/account_collection.rs

@@ -72,6 +72,18 @@ impl RecurseData<'_> {
             },
         );
     }
+
+    fn add_program_id(&mut self, contract_name: &String) {
+        self.add_account(
+            format!("{}_programId", contract_name),
+            &SolanaAccount {
+                loc: Loc::Codegen,
+                is_signer: false,
+                is_writer: false,
+                generated: true,
+            },
+        )
+    }
 }
 
 /// Collect the accounts this contract needs
@@ -333,10 +345,21 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                 // If the one passes the AccountMeta vector to the constructor call, there is no
                 // need to collect accounts for the IDL.
                 if let Some(constructor_no) = constructor_no {
-                    transfer_accounts(loc, *contract_no, *constructor_no, data, false);
+                    transfer_accounts(loc, *contract_no, *constructor_no, data);
+                } else {
+                    data.add_account(
+                        format!("{}_dataAccount", data.contracts[*contract_no].name),
+                        &SolanaAccount {
+                            loc: *loc,
+                            is_signer: false,
+                            is_writer: true,
+                            generated: true,
+                        },
+                    );
                 }
             }
 
+            data.add_program_id(&data.contracts[*contract_no].name);
             data.add_system_account();
         }
         Instr::ExternalCall {
@@ -387,8 +410,15 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
 
             if let Some(accounts) = accounts {
                 accounts.recurse(data, check_expression);
-            } else if let Some((contract_no, function_no)) = contract_function_no {
-                transfer_accounts(loc, *contract_no, *function_no, data, program_id_populated);
+            }
+
+            if let Some((contract_no, function_no)) = contract_function_no {
+                if !program_id_populated {
+                    data.add_program_id(&data.contracts[*contract_no].name);
+                }
+                if accounts.is_none() {
+                    transfer_accounts(loc, *contract_no, *function_no, data);
+                }
             }
         }
         Instr::EmitEvent {
@@ -464,7 +494,6 @@ fn transfer_accounts(
     contract_no: usize,
     function_no: usize,
     data: &mut RecurseData,
-    program_id_present: bool,
 ) {
     let accounts_to_add = data.functions[function_no].solana_accounts.borrow().clone();
 
@@ -515,21 +544,6 @@ fn transfer_accounts(
         data.add_account(name, &account);
     }
 
-    if !program_id_present {
-        data.functions[data.ast_no]
-            .solana_accounts
-            .borrow_mut()
-            .insert(
-                format!("{}_programId", data.contracts[contract_no].name),
-                SolanaAccount {
-                    is_signer: false,
-                    is_writer: false,
-                    generated: true,
-                    loc: *loc,
-                },
-            );
-    }
-
     let cfg_no = data.contracts[contract_no].all_functions[&function_no];
     data.next_queue.insert((contract_no, cfg_no));
     data.next_queue.insert((data.contract_no, data.cfg_func_no));

+ 25 - 0
src/codegen/solana_accounts/account_management.rs

@@ -152,6 +152,31 @@ fn process_instruction(
             };
             *accounts = Some(metas_vector);
         }
+        Instr::Constructor {
+            contract_no,
+            constructor_no: None,
+            accounts,
+            ..
+        } => {
+            let name_to_index = format!("{}_dataAccount", contracts[*contract_no].name);
+            let account_index = functions[ast_no]
+                .solana_accounts
+                .borrow()
+                .get_index_of(&name_to_index)
+                .unwrap();
+            let ptr_to_address = accounts_vector_key_at_index(account_index);
+            let account_metas = vec![account_meta_literal(ptr_to_address, false, true)];
+            let metas_vector = Expression::ArrayLiteral {
+                loc: Loc::Codegen,
+                ty: Type::Array(
+                    Box::new(Type::Struct(StructType::AccountMeta)),
+                    vec![ArrayLength::Fixed(BigInt::from(account_metas.len()))],
+                ),
+                dimensions: vec![1],
+                values: account_metas,
+            };
+            *accounts = Some(metas_vector);
+        }
         Instr::AccountAccess { loc, name, var_no } => {
             // This could have been an Expression::AccountAccess if we had a three-address form.
             // The amount of code necessary to traverse all Instructions and all expressions recursively

+ 15 - 7
src/codegen/statements/mod.rs

@@ -12,16 +12,15 @@ use super::{
     yul::inline_assembly_cfg,
     Builtin, Expression, Options,
 };
-use crate::sema::{
-    ast::{
-        self, ArrayLength, DestructureField, Function, Namespace, RetrieveType, Statement, Type,
-        Type::Uint,
-    },
-    Recurse,
+use crate::sema::ast::{
+    self, ArrayLength, DestructureField, Function, Namespace, RetrieveType, SolanaAccount,
+    Statement, Type, Type::Uint,
 };
+use crate::sema::solana_accounts::BuiltinAccounts;
+use crate::sema::Recurse;
 use num_bigint::BigInt;
 use num_traits::Zero;
-use solang_parser::pt::{self, CodeLocation, Loc::Codegen};
+use solang_parser::pt::{self, CodeLocation, Loc, Loc::Codegen};
 
 mod try_catch;
 
@@ -1154,6 +1153,15 @@ impl Namespace {
 
         func.body = vec![Statement::Return(Codegen, None)];
         func.has_body = true;
+        func.solana_accounts.borrow_mut().insert(
+            BuiltinAccounts::DataAccount.to_string(),
+            SolanaAccount {
+                loc: Loc::Codegen,
+                is_signer: false,
+                is_writer: true,
+                generated: true,
+            },
+        );
 
         func
     }

+ 4 - 9
src/emit/solana/target.rs

@@ -1227,20 +1227,15 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         binary: &Binary<'b>,
         function: FunctionValue<'b>,
         _success: Option<&mut BasicValueEnum<'b>>,
-        contract_no: usize,
-        _address: PointerValue<'b>,
+        _contract_no: usize,
+        address: PointerValue<'b>,
         encoded_args: BasicValueEnum<'b>,
         encoded_args_len: BasicValueEnum<'b>,
         mut contract_args: ContractArgs<'b>,
-        ns: &ast::Namespace,
+        _ns: &ast::Namespace,
         _loc: Loc,
     ) {
-        let const_program_id = binary.emit_global_string(
-            "const_program_id",
-            ns.contracts[contract_no].program_id.as_ref().unwrap(),
-            true,
-        );
-        contract_args.program_id = Some(const_program_id);
+        contract_args.program_id = Some(address);
 
         let payload = binary.vector_bytes(encoded_args);
         let payload_len = encoded_args_len.into_int_value();

+ 8 - 4
src/sema/ast.rs

@@ -804,14 +804,17 @@ impl Contract {
 
     /// Does the constructor require arguments. Should be false is there is no constructor
     pub fn constructor_needs_arguments(&self, ns: &Namespace) -> bool {
-        self.have_constructor(ns) && self.no_args_constructor(ns).is_none()
+        !self.constructors(ns).is_empty() && self.no_args_constructor(ns).is_none()
     }
 
-    /// Does the contract have a constructor defined
-    pub fn have_constructor(&self, ns: &Namespace) -> bool {
+    /// Does the contract have a constructor defined?
+    /// Returns all the constructor function numbers if any
+    pub fn constructors(&self, ns: &Namespace) -> Vec<usize> {
         self.functions
             .iter()
-            .any(|func_no| ns.functions[*func_no].is_constructor())
+            .copied()
+            .filter(|func_no| ns.functions[*func_no].is_constructor())
+            .collect::<Vec<usize>>()
     }
 
     /// Return the constructor with no arguments
@@ -1226,6 +1229,7 @@ pub struct CallArgs {
     pub accounts: Option<Box<Expression>>,
     pub seeds: Option<Box<Expression>>,
     pub flags: Option<Box<Expression>>,
+    pub program_id: Option<Box<Expression>>,
 }
 
 impl Recurse for CallArgs {

+ 3 - 2
src/sema/builtin.rs

@@ -10,6 +10,7 @@ use super::expression::{ExprContext, ResolveTo};
 use super::symtable::Symtable;
 use crate::sema::ast::{RetrieveType, Tag, UserTypeDecl};
 use crate::sema::expression::resolve_expression::expression;
+use crate::sema::namespace::ResolveTypeContext;
 use crate::Target;
 use num_bigint::BigInt;
 use num_traits::One;
@@ -1082,7 +1083,7 @@ pub(super) fn resolve_namespace_call(
                         let ty = ns.resolve_type(
                             context.file_no,
                             context.contract_no,
-                            false,
+                            ResolveTypeContext::None,
                             &param.ty,
                             diagnostics,
                         )?;
@@ -1123,7 +1124,7 @@ pub(super) fn resolve_namespace_call(
                 let ty = ns.resolve_type(
                     context.file_no,
                     context.contract_no,
-                    false,
+                    ResolveTypeContext::None,
                     args[1].remove_parenthesis(),
                     diagnostics,
                 )?;

+ 1 - 1
src/sema/contracts.rs

@@ -1090,7 +1090,7 @@ fn check_base_args(contract_no: usize, ns: &mut ast::Namespace) {
         })
         .collect::<Vec<usize>>();
 
-    if contract.have_constructor(ns) {
+    if !contract.constructors(ns).is_empty() {
         for constructor_no in contract
             .functions
             .iter()

+ 67 - 20
src/sema/expression/constructor.rs

@@ -5,9 +5,9 @@ use crate::sema::diagnostics::Diagnostics;
 use crate::sema::expression::function_call::{collect_call_args, parse_call_args};
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::expression::{ExprContext, ResolveTo};
+use crate::sema::namespace::ResolveTypeContext;
 use crate::sema::symtable::Symtable;
 use crate::sema::unused_variable::used_variable;
-use crate::Target;
 use solang_parser::diagnostics::Diagnostic;
 use solang_parser::pt;
 use solang_parser::pt::{CodeLocation, Visibility};
@@ -60,8 +60,6 @@ fn constructor(
         return Err(());
     }
 
-    solana_constructor_check(loc, no, diagnostics, context, &call_args, ns);
-
     // check for circular references
     if circular_reference(no, context_contract_no, ns) {
         diagnostics.push(Diagnostic::error(
@@ -214,9 +212,13 @@ pub fn constructor_named_args(
 ) -> Result<Expression, ()> {
     let (ty, call_args, _) = collect_call_args(ty, diagnostics)?;
 
-    let call_args = parse_call_args(loc, &call_args, false, context, ns, symtable, diagnostics)?;
-
-    let no = match ns.resolve_type(context.file_no, context.contract_no, false, ty, diagnostics)? {
+    let no = match ns.resolve_type(
+        context.file_no,
+        context.contract_no,
+        ResolveTypeContext::None,
+        ty,
+        diagnostics,
+    )? {
         Type::Contract(n) => n,
         _ => {
             diagnostics.push(Diagnostic::error(*loc, "contract expected".to_string()));
@@ -224,6 +226,17 @@ pub fn constructor_named_args(
         }
     };
 
+    let call_args = parse_call_args(
+        loc,
+        &call_args,
+        Some(no),
+        false,
+        context,
+        ns,
+        symtable,
+        diagnostics,
+    )?;
+
     // The current contract cannot be constructed with new. In order to create
     // the contract, we need the code hash of the contract. Part of that code
     // will be code we're emitted here. So we end up with a crypto puzzle.
@@ -260,8 +273,6 @@ pub fn constructor_named_args(
         return Err(());
     }
 
-    solana_constructor_check(loc, no, diagnostics, context, &call_args, ns);
-
     // check for circular references
     if circular_reference(no, context_contract_no, ns) {
         diagnostics.push(Diagnostic::error(
@@ -436,7 +447,13 @@ pub fn new(
         ty
     };
 
-    let ty = ns.resolve_type(context.file_no, context.contract_no, false, ty, diagnostics)?;
+    let ty = ns.resolve_type(
+        context.file_no,
+        context.contract_no,
+        ResolveTypeContext::None,
+        ty,
+        diagnostics,
+    )?;
 
     match &ty {
         Type::Array(ty, dim) => {
@@ -461,8 +478,16 @@ pub fn new(
         }
         Type::String | Type::DynamicBytes => {}
         Type::Contract(n) => {
-            let call_args =
-                parse_call_args(loc, &call_args, false, context, ns, symtable, diagnostics)?;
+            let call_args = parse_call_args(
+                loc,
+                &call_args,
+                Some(*n),
+                false,
+                context,
+                ns,
+                symtable,
+                diagnostics,
+            )?;
 
             return constructor(loc, *n, args, call_args, context, ns, symtable, diagnostics);
         }
@@ -577,29 +602,51 @@ pub(super) fn deprecated_constructor_arguments(
 
 /// When calling a constructor on Solana, we must verify it the contract we are instantiating has
 /// a program id annotation and require the accounts call argument if the call is inside a loop.
-fn solana_constructor_check(
+pub(super) fn solana_constructor_check(
     loc: &pt::Loc,
     constructor_contract_no: usize,
     diagnostics: &mut Diagnostics,
     context: &ExprContext,
     call_args: &CallArgs,
-    ns: &Namespace,
+    ns: &mut Namespace,
 ) {
-    if ns.target != Target::Solana {
-        return;
-    }
-
-    if ns.contracts[constructor_contract_no].program_id.is_none() {
+    if !ns.contracts[constructor_contract_no].instantiable {
         diagnostics.push(Diagnostic::error(
             *loc,
             format!(
-                "in order to instantiate contract '{}', a @program_id is required on contract '{}'",
+                "cannot construct '{}' of type '{}'",
                 ns.contracts[constructor_contract_no].name,
-                ns.contracts[constructor_contract_no].name
+                ns.contracts[constructor_contract_no].ty
             ),
         ));
     }
 
+    if let Some(context_contract) = context.contract_no {
+        if circular_reference(constructor_contract_no, context_contract, ns) {
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                format!(
+                    "circular reference creating contract '{}'",
+                    ns.contracts[constructor_contract_no].name
+                ),
+            ));
+        }
+
+        if !ns.contracts[context_contract]
+            .creates
+            .contains(&constructor_contract_no)
+        {
+            ns.contracts[context_contract]
+                .creates
+                .push(constructor_contract_no);
+        }
+    } else {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "constructors not allowed in free standing functions".to_string(),
+        ));
+    }
+
     if !context.in_a_loop() || call_args.accounts.is_some() {
         return;
     }

+ 721 - 354
src/sema/expression/function_call.rs

@@ -6,15 +6,19 @@ use crate::sema::ast::{
 };
 use crate::sema::contracts::is_base;
 use crate::sema::diagnostics::Diagnostics;
-use crate::sema::expression::constructor::{deprecated_constructor_arguments, new};
+use crate::sema::expression::constructor::{
+    deprecated_constructor_arguments, new, solana_constructor_check,
+};
 use crate::sema::expression::literals::{named_struct_literal, struct_literal};
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::expression::{ExprContext, ResolveTo};
 use crate::sema::format::string_format;
+use crate::sema::namespace::ResolveTypeContext;
 use crate::sema::symtable::Symtable;
 use crate::sema::unused_variable::check_function_call;
 use crate::sema::{builtin, using};
 use crate::Target;
+use num_bigint::{BigInt, Sign};
 use solang_parser::diagnostics::Diagnostic;
 use solang_parser::pt;
 use solang_parser::pt::{CodeLocation, Loc, Visibility};
@@ -101,7 +105,16 @@ pub(super) fn call_function_type(
         mutability,
     } = ty
     {
-        let call_args = parse_call_args(loc, call_args, true, context, ns, symtable, diagnostics)?;
+        let call_args = parse_call_args(
+            loc,
+            call_args,
+            None,
+            true,
+            context,
+            ns,
+            symtable,
+            diagnostics,
+        )?;
 
         if let Some(value) = &call_args.value {
             if !value.const_zero(ns) && !matches!(mutability, Mutability::Payable(_)) {
@@ -495,6 +508,7 @@ fn try_namespace(
     var: &pt::Expression,
     func: &pt::Identifier,
     args: &[pt::Expression],
+    call_args: &[&pt::NamedArgument],
     call_args_loc: Option<pt::Loc>,
     context: &ExprContext,
     ns: &mut Namespace,
@@ -592,7 +606,22 @@ fn try_namespace(
             // is a base contract of us
             if let Some(contract_no) = context.contract_no {
                 if is_base(call_contract_no, contract_no, ns) {
-                    if let Some(loc) = call_args_loc {
+                    if ns.target == Target::Solana && call_args_loc.is_some() {
+                        // On Solana, assume this is an external call
+                        return contract_call_pos_args(
+                            loc,
+                            call_contract_no,
+                            func,
+                            None,
+                            args,
+                            call_args,
+                            context,
+                            ns,
+                            symtable,
+                            diagnostics,
+                            resolve_to,
+                        );
+                    } else if let Some(loc) = call_args_loc {
                         diagnostics.push(Diagnostic::error(
                             loc,
                             "call arguments not allowed on internal calls".to_string(),
@@ -619,13 +648,31 @@ fn try_namespace(
                         symtable,
                         diagnostics,
                     )?));
-                } else {
+                } else if ns.target != Target::Solana {
                     diagnostics.push(Diagnostic::error(
                         *loc,
                         "function calls via contract name are only valid for base contracts".into(),
                     ));
                 }
             }
+
+            if ns.target == Target::Solana {
+                // If the symbol resolves to a contract, this is an external call on Solana
+                // regardless of whether we are inside a contract or not.
+                return contract_call_pos_args(
+                    loc,
+                    call_contract_no,
+                    func,
+                    None,
+                    args,
+                    call_args,
+                    context,
+                    ns,
+                    symtable,
+                    diagnostics,
+                    resolve_to,
+                );
+            }
         }
     }
 
@@ -873,7 +920,7 @@ fn try_user_type(
     if let Ok(Type::UserType(no)) = ns.resolve_type(
         context.file_no,
         context.contract_no,
-        false,
+        ResolveTypeContext::None,
         var,
         &mut Diagnostics::default(),
     ) {
@@ -1069,148 +1116,19 @@ fn try_type_method(
         }
 
         Type::Contract(ext_contract_no) => {
-            let call_args =
-                parse_call_args(loc, call_args, true, context, ns, symtable, diagnostics)?;
-
-            let mut errors = Diagnostics::default();
-            let mut name_matches: Vec<usize> = Vec::new();
-
-            for function_no in ns.contracts[*ext_contract_no].all_functions.keys() {
-                if func.name != ns.functions[*function_no].name
-                    || ns.functions[*function_no].ty != pt::FunctionTy::Function
-                {
-                    continue;
-                }
-
-                name_matches.push(*function_no);
-            }
-
-            for function_no in &name_matches {
-                let params_len = ns.functions[*function_no].params.len();
-
-                if params_len != args.len() {
-                    errors.push(Diagnostic::error(
-                        *loc,
-                        format!(
-                            "function expects {} arguments, {} provided",
-                            params_len,
-                            args.len()
-                        ),
-                    ));
-                    continue;
-                }
-
-                let mut matches = true;
-                let mut cast_args = Vec::new();
-
-                // check if arguments can be implicitly casted
-                for (i, arg) in args.iter().enumerate() {
-                    let ty = ns.functions[*function_no].params[i].ty.clone();
-
-                    let arg = match expression(
-                        arg,
-                        context,
-                        ns,
-                        symtable,
-                        &mut errors,
-                        ResolveTo::Type(&ty),
-                    ) {
-                        Ok(e) => e,
-                        Err(_) => {
-                            matches = false;
-                            continue;
-                        }
-                    };
-
-                    match arg.cast(&arg.loc(), &ty, true, ns, &mut errors) {
-                        Ok(expr) => cast_args.push(expr),
-                        Err(()) => {
-                            matches = false;
-                            continue;
-                        }
-                    }
-                }
-
-                if matches {
-                    if !ns.functions[*function_no].is_public() {
-                        diagnostics.push(Diagnostic::error(
-                            *loc,
-                            format!("function '{}' is not 'public' or 'external'", func.name),
-                        ));
-                        return Err(());
-                    }
-
-                    if let Some(value) = &call_args.value {
-                        if !value.const_zero(ns) && !ns.functions[*function_no].is_payable() {
-                            diagnostics.push(Diagnostic::error(
-                                *loc,
-                                format!(
-                                    "sending value to function '{}' which is not payable",
-                                    func.name
-                                ),
-                            ));
-                            return Err(());
-                        }
-                    }
-
-                    let func = &ns.functions[*function_no];
-                    let returns = function_returns(func, resolve_to);
-                    let ty = function_type(func, true, resolve_to);
-
-                    return Ok(Some(Expression::ExternalFunctionCall {
-                        loc: *loc,
-                        returns,
-                        function: Box::new(Expression::ExternalFunction {
-                            loc: *loc,
-                            ty,
-                            function_no: *function_no,
-                            address: Box::new(var_expr.cast(
-                                &var.loc(),
-                                &Type::Contract(func.contract_no.unwrap()),
-                                true,
-                                ns,
-                                diagnostics,
-                            )?),
-                        }),
-                        args: cast_args,
-                        call_args,
-                    }));
-                } else if name_matches.len() > 1 && diagnostics.extend_non_casting(&errors) {
-                    return Err(());
-                }
-            }
-
-            // what about call args
-            match using::try_resolve_using_call(
+            return contract_call_pos_args(
                 loc,
+                *ext_contract_no,
                 func,
-                var_expr,
-                context,
+                Some(var_expr),
                 args,
+                call_args,
+                context,
+                ns,
                 symtable,
                 diagnostics,
-                ns,
                 resolve_to,
-            ) {
-                Ok(Some(expr)) => {
-                    return Ok(Some(expr));
-                }
-                Ok(None) => (),
-                Err(_) => {
-                    return Err(());
-                }
-            }
-
-            if name_matches.len() == 1 {
-                diagnostics.extend(errors);
-            } else if name_matches.len() != 1 {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    "cannot find overloaded function which matches signature".to_string(),
-                ));
-            }
-
-            return Err(());
+            );
         }
 
         Type::Address(is_payable) => {
@@ -1287,8 +1205,16 @@ fn try_type_method(
             };
 
             if let Some(ty) = ty {
-                let call_args =
-                    parse_call_args(loc, call_args, true, context, ns, symtable, diagnostics)?;
+                let call_args = parse_call_args(
+                    loc,
+                    call_args,
+                    None,
+                    true,
+                    context,
+                    ns,
+                    symtable,
+                    diagnostics,
+                )?;
 
                 if ty != CallTy::Regular && call_args.value.is_some() {
                     diagnostics.push(Diagnostic::error(
@@ -1389,6 +1315,7 @@ pub(super) fn method_call_pos_args(
         var,
         func,
         args,
+        call_args,
         call_args_loc,
         context,
         ns,
@@ -1397,6 +1324,7 @@ pub(super) fn method_call_pos_args(
         resolve_to,
     )? {
         return Ok(resolved_call);
+    } else {
     }
 
     if let Some(resolved_call) = try_user_type(
@@ -1429,6 +1357,26 @@ pub(super) fn method_call_pos_args(
             &path,
             &mut Diagnostics::default(),
         ) {
+            if let Some(callee_contract) =
+                is_solana_external_call(&list, context.contract_no, &call_args_loc, ns)
+            {
+                if let Some(resolved_call) = contract_call_pos_args(
+                    &var.loc(),
+                    callee_contract,
+                    func,
+                    None,
+                    args,
+                    call_args,
+                    context,
+                    ns,
+                    symtable,
+                    diagnostics,
+                    resolve_to,
+                )? {
+                    return Ok(resolved_call);
+                }
+            }
+
             if let Some(loc) = call_args_loc {
                 diagnostics.push(Diagnostic::error(
                     loc,
@@ -1620,7 +1568,22 @@ pub(super) fn method_call_named_args(
             // is a base contract of us
             if let Some(contract_no) = context.contract_no {
                 if is_base(call_contract_no, contract_no, ns) {
-                    if let Some(loc) = call_args_loc {
+                    if ns.target == Target::Solana && call_args_loc.is_some() {
+                        // If on Solana, assume this is an external call
+                        return contract_call_named_args(
+                            loc,
+                            None,
+                            func_name,
+                            args,
+                            call_args,
+                            call_contract_no,
+                            context,
+                            symtable,
+                            ns,
+                            diagnostics,
+                            resolve_to,
+                        );
+                    } else if let Some(loc) = call_args_loc {
                         diagnostics.push(Diagnostic::error(
                             loc,
                             "call arguments not allowed on internal calls".to_string(),
@@ -1646,13 +1609,31 @@ pub(super) fn method_call_named_args(
                         symtable,
                         diagnostics,
                     );
-                } else {
+                } else if ns.target != Target::Solana {
                     diagnostics.push(Diagnostic::error(
                         *loc,
                         "function calls via contract name are only valid for base contracts".into(),
                     ));
                 }
             }
+
+            if ns.target == Target::Solana {
+                // If the identifier symbol resolves to a contract, this an external call on Solana
+                // regardless of whether we are inside a contract or not.
+                return contract_call_named_args(
+                    loc,
+                    None,
+                    func_name,
+                    args,
+                    call_args,
+                    call_contract_no,
+                    context,
+                    symtable,
+                    ns,
+                    diagnostics,
+                    resolve_to,
+                );
+            }
         }
     }
 
@@ -1664,6 +1645,24 @@ pub(super) fn method_call_named_args(
             &path,
             &mut Diagnostics::default(),
         ) {
+            if let Some(callee_contract) =
+                is_solana_external_call(&list, context.contract_no, &call_args_loc, ns)
+            {
+                return contract_call_named_args(
+                    &var.loc(),
+                    None,
+                    func_name,
+                    args,
+                    call_args,
+                    callee_contract,
+                    context,
+                    symtable,
+                    ns,
+                    diagnostics,
+                    resolve_to,
+                );
+            }
+
             if let Some(loc) = call_args_loc {
                 diagnostics.push(Diagnostic::error(
                     loc,
@@ -1690,192 +1689,19 @@ pub(super) fn method_call_named_args(
     let var_ty = var_expr.ty();
 
     if let Type::Contract(external_contract_no) = &var_ty.deref_any() {
-        let call_args = parse_call_args(loc, call_args, true, context, ns, symtable, diagnostics)?;
-
-        let mut arguments = HashMap::new();
-
-        // check if the arguments are not garbage
-        for arg in args {
-            if arguments.contains_key(arg.name.name.as_str()) {
-                diagnostics.push(Diagnostic::error(
-                    arg.name.loc,
-                    format!("duplicate argument with name '{}'", arg.name.name),
-                ));
-
-                let _ = expression(
-                    &arg.expr,
-                    context,
-                    ns,
-                    symtable,
-                    diagnostics,
-                    ResolveTo::Unknown,
-                );
-
-                continue;
-            }
-
-            arguments.insert(arg.name.name.as_str(), &arg.expr);
-        }
-
-        let mut errors = Diagnostics::default();
-        let mut name_matches: Vec<usize> = Vec::new();
-
-        // function call
-        for function_no in ns.contracts[*external_contract_no].all_functions.keys() {
-            if ns.functions[*function_no].name != func_name.name
-                || ns.functions[*function_no].ty != pt::FunctionTy::Function
-            {
-                continue;
-            }
-
-            name_matches.push(*function_no);
-        }
-
-        for function_no in &name_matches {
-            let func = &ns.functions[*function_no];
-
-            let unnamed_params = func.params.iter().filter(|p| p.id.is_none()).count();
-            let params_len = func.params.len();
-
-            let mut matches = true;
-
-            if unnamed_params > 0 {
-                errors.push(Diagnostic::cast_error_with_note(
-                    *loc,
-                    format!(
-                        "function cannot be called with named arguments as {unnamed_params} of its parameters do not have names"
-                    ),
-                    func.loc,
-                    format!("definition of {}", func.name),
-                ));
-                matches = false;
-            } else if params_len != args.len() {
-                errors.push(Diagnostic::cast_error(
-                    *loc,
-                    format!(
-                        "function expects {} arguments, {} provided",
-                        params_len,
-                        args.len()
-                    ),
-                ));
-                matches = false;
-            }
-            let mut cast_args = Vec::new();
-
-            for i in 0..params_len {
-                let param = ns.functions[*function_no].params[i].clone();
-                if param.id.is_none() {
-                    continue;
-                }
-
-                let arg = match arguments.get(param.name_as_str()) {
-                    Some(a) => a,
-                    None => {
-                        matches = false;
-                        diagnostics.push(Diagnostic::cast_error(
-                            *loc,
-                            format!(
-                                "missing argument '{}' to function '{}'",
-                                param.name_as_str(),
-                                func_name.name,
-                            ),
-                        ));
-                        continue;
-                    }
-                };
-
-                let arg = match expression(
-                    arg,
-                    context,
-                    ns,
-                    symtable,
-                    &mut errors,
-                    ResolveTo::Type(&param.ty),
-                ) {
-                    Ok(e) => e,
-                    Err(()) => {
-                        matches = false;
-                        continue;
-                    }
-                };
-
-                match arg.cast(&arg.loc(), &param.ty, true, ns, &mut errors) {
-                    Ok(expr) => cast_args.push(expr),
-                    Err(()) => {
-                        matches = false;
-                        break;
-                    }
-                }
-            }
-
-            if matches {
-                if !ns.functions[*function_no].is_public() {
-                    diagnostics.push(Diagnostic::error(
-                        *loc,
-                        format!(
-                            "function '{}' is not 'public' or 'external'",
-                            func_name.name
-                        ),
-                    ));
-                } else if let Some(value) = &call_args.value {
-                    if !value.const_zero(ns) && !ns.functions[*function_no].is_payable() {
-                        diagnostics.push(Diagnostic::error(
-                            *loc,
-                            format!(
-                                "sending value to function '{}' which is not payable",
-                                func_name.name
-                            ),
-                        ));
-                    }
-                }
-
-                let func = &ns.functions[*function_no];
-                let returns = function_returns(func, resolve_to);
-                let ty = function_type(func, true, resolve_to);
-
-                return Ok(Expression::ExternalFunctionCall {
-                    loc: *loc,
-                    returns,
-                    function: Box::new(Expression::ExternalFunction {
-                        loc: *loc,
-                        ty,
-                        function_no: *function_no,
-                        address: Box::new(var_expr.cast(
-                            &var.loc(),
-                            &Type::Contract(func.contract_no.unwrap()),
-                            true,
-                            ns,
-                            diagnostics,
-                        )?),
-                    }),
-                    args: cast_args,
-                    call_args,
-                });
-            } else if name_matches.len() > 1 && diagnostics.extend_non_casting(&errors) {
-                return Err(());
-            }
-        }
-
-        match name_matches.len() {
-            0 => {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    format!(
-                        "contract '{}' does not have function '{}'",
-                        var_ty.deref_any().to_string(ns),
-                        func_name.name
-                    ),
-                ));
-            }
-            1 => diagnostics.extend(errors),
-            _ => {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    "cannot find overloaded function which matches signature".to_string(),
-                ));
-            }
-        }
-        return Err(());
+        return contract_call_named_args(
+            loc,
+            Some(var_expr),
+            func_name,
+            args,
+            call_args,
+            *external_contract_no,
+            context,
+            symtable,
+            ns,
+            diagnostics,
+            resolve_to,
+        );
     }
 
     diagnostics.push(Diagnostic::error(
@@ -1941,6 +1767,7 @@ pub fn collect_call_args<'a>(
 pub(super) fn parse_call_args(
     loc: &pt::Loc,
     call_args: &[&pt::NamedArgument],
+    callee_contract: Option<usize>,
     external_call: bool,
     context: &ExprContext,
     ns: &mut Namespace,
@@ -2148,13 +1975,37 @@ pub(super) fn parse_call_args(
 
                 res.seeds = Some(Box::new(expr));
             }
-            "flags" => {
-                if !(ns.target.is_polkadot() && external_call) {
+            "program_id" => {
+                if ns.target != Target::Solana {
                     diagnostics.push(Diagnostic::error(
                         arg.loc,
-                        "'flags' are only permitted for external calls on polkadot".into(),
-                    ));
-                    return Err(());
+                        format!(
+                            "'program_id' not permitted for external calls or constructors on {}",
+                            ns.target
+                        ),
+                    ));
+                    return Err(());
+                }
+
+                let ty = Type::Address(false);
+                let expr = expression(
+                    &arg.expr,
+                    context,
+                    ns,
+                    symtable,
+                    diagnostics,
+                    ResolveTo::Type(&ty),
+                )?;
+
+                res.program_id = Some(Box::new(expr));
+            }
+            "flags" => {
+                if !(ns.target.is_polkadot() && external_call) {
+                    diagnostics.push(Diagnostic::error(
+                        arg.loc,
+                        "'flags' are only permitted for external calls on polkadot".into(),
+                    ));
+                    return Err(());
                 }
 
                 let ty = Type::Uint(32);
@@ -2179,24 +2030,37 @@ pub(super) fn parse_call_args(
         }
     }
 
-    // address is required on solana constructors
-    if ns.target == Target::Solana
-        && !external_call
-        && res.accounts.is_none()
-        && !matches!(
-            ns.functions[context.function_no.unwrap()].visibility,
-            Visibility::External(_)
-        )
-        && !ns.functions[context.function_no.unwrap()].is_constructor()
-    {
-        diagnostics.push(Diagnostic::error(
-            *loc,
-            "accounts are required for calling a contract. You can either provide the \
+    if ns.target == Target::Solana {
+        if !external_call
+            && res.accounts.is_none()
+            && !matches!(
+                ns.functions[context.function_no.unwrap()].visibility,
+                Visibility::External(_)
+            )
+            && !ns.functions[context.function_no.unwrap()].is_constructor()
+        {
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                "accounts are required for calling a contract. You can either provide the \
             accounts with the {accounts: ...} call argument or change this function's \
             visibility to external"
-                .to_string(),
-        ));
-        return Err(());
+                    .to_string(),
+            ));
+            return Err(());
+        }
+
+        if let Some(callee_contract_no) = callee_contract {
+            if res.program_id.is_none() && ns.contracts[callee_contract_no].program_id.is_none() {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "a contract needs a program id to be called. Either a '@program_id' \
+                        must be declared above a contract or the {program_id: ...} call argument \
+                        must be present"
+                        .to_string(),
+                ));
+                return Err(());
+            }
+        }
     }
 
     Ok(res)
@@ -2219,7 +2083,7 @@ pub fn named_call_expr(
     match ns.resolve_type(
         context.file_no,
         context.contract_no,
-        true,
+        ResolveTypeContext::Casting,
         ty,
         &mut nullsink,
     ) {
@@ -2286,7 +2150,7 @@ pub fn call_expr(
     match ns.resolve_type(
         context.file_no,
         context.contract_no,
-        true,
+        ResolveTypeContext::Casting,
         ty,
         &mut nullsink,
     ) {
@@ -2655,3 +2519,506 @@ fn resolve_internal_call(
         args: cast_args,
     })
 }
+
+/// Resolve call to contract with named arguments
+fn contract_call_named_args(
+    loc: &pt::Loc,
+    var_expr: Option<Expression>,
+    func_name: &pt::Identifier,
+    args: &[pt::NamedArgument],
+    call_args: &[&pt::NamedArgument],
+    external_contract_no: usize,
+    context: &ExprContext,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+    diagnostics: &mut Diagnostics,
+    resolve_to: ResolveTo,
+) -> Result<Expression, ()> {
+    let mut arguments = HashMap::new();
+
+    // check if the arguments are not garbage
+    for arg in args {
+        if arguments.contains_key(arg.name.name.as_str()) {
+            diagnostics.push(Diagnostic::error(
+                arg.name.loc,
+                format!("duplicate argument with name '{}'", arg.name.name),
+            ));
+
+            let _ = expression(
+                &arg.expr,
+                context,
+                ns,
+                symtable,
+                diagnostics,
+                ResolveTo::Unknown,
+            );
+
+            continue;
+        }
+
+        arguments.insert(arg.name.name.as_str(), &arg.expr);
+    }
+
+    let (call_args, name_matches) = match preprocess_contract_call(
+        loc,
+        call_args,
+        external_contract_no,
+        func_name,
+        args,
+        context,
+        ns,
+        symtable,
+        diagnostics,
+    ) {
+        PreProcessedCall::Success {
+            call_args,
+            name_matches,
+        } => (call_args, name_matches),
+        PreProcessedCall::DefaultConstructor(expr) => return Ok(expr),
+        PreProcessedCall::Error => return Err(()),
+    };
+
+    let mut errors = Diagnostics::default();
+
+    for function_no in &name_matches {
+        let func = &ns.functions[*function_no];
+
+        let unnamed_params = func.params.iter().filter(|p| p.id.is_none()).count();
+        let params_len = func.params.len();
+
+        let mut matches = true;
+
+        if unnamed_params > 0 {
+            errors.push(Diagnostic::cast_error_with_note(
+                *loc,
+                format!(
+                    "function cannot be called with named arguments as {unnamed_params} of its parameters do not have names"
+                ),
+                func.loc,
+                format!("definition of {}", func.name),
+            ));
+            matches = false;
+        } else if params_len != args.len() {
+            errors.push(Diagnostic::cast_error(
+                *loc,
+                format!(
+                    "function expects {} arguments, {} provided",
+                    params_len,
+                    args.len()
+                ),
+            ));
+            matches = false;
+        }
+        let mut cast_args = Vec::new();
+
+        for i in 0..params_len {
+            let param = ns.functions[*function_no].params[i].clone();
+            if param.id.is_none() {
+                continue;
+            }
+
+            let arg = match arguments.get(param.name_as_str()) {
+                Some(a) => a,
+                None => {
+                    matches = false;
+                    diagnostics.push(Diagnostic::cast_error(
+                        *loc,
+                        format!(
+                            "missing argument '{}' to function '{}'",
+                            param.name_as_str(),
+                            func_name.name,
+                        ),
+                    ));
+                    continue;
+                }
+            };
+
+            let arg = match expression(
+                arg,
+                context,
+                ns,
+                symtable,
+                &mut errors,
+                ResolveTo::Type(&param.ty),
+            ) {
+                Ok(e) => e,
+                Err(()) => {
+                    matches = false;
+                    continue;
+                }
+            };
+
+            match arg.cast(&arg.loc(), &param.ty, true, ns, &mut errors) {
+                Ok(expr) => cast_args.push(expr),
+                Err(()) => {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+
+        if matches {
+            return contract_call_match(
+                loc,
+                func_name,
+                *function_no,
+                external_contract_no,
+                call_args,
+                cast_args,
+                var_expr.as_ref(),
+                ns,
+                diagnostics,
+                resolve_to,
+            );
+        } else if name_matches.len() > 1 && diagnostics.extend_non_casting(&errors) {
+            return Err(());
+        }
+    }
+
+    match name_matches.len() {
+        0 => {
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                format!(
+                    "contract '{}' does not have function '{}'",
+                    ns.contracts[external_contract_no].name, func_name.name
+                ),
+            ));
+        }
+        1 => diagnostics.extend(errors),
+        _ => {
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                "cannot find overloaded function which matches signature".to_string(),
+            ));
+        }
+    }
+    Err(())
+}
+
+/// Resolve call to contract with positional arguments
+fn contract_call_pos_args(
+    loc: &pt::Loc,
+    external_contract_no: usize,
+    func: &pt::Identifier,
+    var_expr: Option<&Expression>,
+    args: &[pt::Expression],
+    call_args: &[&pt::NamedArgument],
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+    resolve_to: ResolveTo,
+) -> Result<Option<Expression>, ()> {
+    let (call_args, name_matches) = match preprocess_contract_call(
+        loc,
+        call_args,
+        external_contract_no,
+        func,
+        args,
+        context,
+        ns,
+        symtable,
+        diagnostics,
+    ) {
+        PreProcessedCall::Success {
+            call_args,
+            name_matches,
+        } => (call_args, name_matches),
+        PreProcessedCall::DefaultConstructor(expr) => return Ok(Some(expr)),
+        PreProcessedCall::Error => return Err(()),
+    };
+
+    let mut errors = Diagnostics::default();
+
+    for function_no in &name_matches {
+        let params_len = ns.functions[*function_no].params.len();
+
+        if params_len != args.len() {
+            errors.push(Diagnostic::error(
+                *loc,
+                format!(
+                    "function expects {} arguments, {} provided",
+                    params_len,
+                    args.len()
+                ),
+            ));
+            continue;
+        }
+
+        let mut matches = true;
+        let mut cast_args = Vec::new();
+
+        // check if arguments can be implicitly casted
+        for (i, arg) in args.iter().enumerate() {
+            let ty = ns.functions[*function_no].params[i].ty.clone();
+
+            let arg = match expression(
+                arg,
+                context,
+                ns,
+                symtable,
+                &mut errors,
+                ResolveTo::Type(&ty),
+            ) {
+                Ok(e) => e,
+                Err(_) => {
+                    matches = false;
+                    continue;
+                }
+            };
+
+            match arg.cast(&arg.loc(), &ty, true, ns, &mut errors) {
+                Ok(expr) => cast_args.push(expr),
+                Err(()) => {
+                    matches = false;
+                    continue;
+                }
+            }
+        }
+
+        if matches {
+            let resolved_call = contract_call_match(
+                loc,
+                func,
+                *function_no,
+                external_contract_no,
+                call_args,
+                cast_args,
+                var_expr,
+                ns,
+                diagnostics,
+                resolve_to,
+            )?;
+            return Ok(Some(resolved_call));
+        } else if name_matches.len() > 1 && diagnostics.extend_non_casting(&errors) {
+            return Err(());
+        }
+    }
+
+    if let Some(var) = var_expr {
+        // what about call args
+        match using::try_resolve_using_call(
+            loc,
+            func,
+            var,
+            context,
+            args,
+            symtable,
+            diagnostics,
+            ns,
+            resolve_to,
+        ) {
+            Ok(Some(expr)) => {
+                return Ok(Some(expr));
+            }
+            Ok(None) => (),
+            Err(_) => {
+                return Err(());
+            }
+        }
+    }
+
+    if name_matches.len() == 1 {
+        diagnostics.extend(errors);
+    } else if name_matches.len() != 1 {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot find overloaded function which matches signature".to_string(),
+        ));
+    }
+
+    Err(())
+}
+
+/// Checks if an identifier path is an external call on Solana.
+/// For instance, my_file.my_contract.my_func() may be a call to a contract.
+fn is_solana_external_call(
+    list: &[(pt::Loc, usize)],
+    contract_no: Option<usize>,
+    call_args_loc: &Option<pt::Loc>,
+    ns: &Namespace,
+) -> Option<usize> {
+    if ns.target == Target::Solana
+        && list.len() == 1
+        && ns.functions[list[0].1].contract_no != contract_no
+    {
+        if let (Some(callee), Some(caller)) = (ns.functions[list[0].1].contract_no, contract_no) {
+            if is_base(callee, caller, ns) && call_args_loc.is_none() {
+                return None;
+            }
+        }
+        return ns.functions[list[0].1].contract_no;
+    }
+
+    None
+}
+
+/// Data structure to manage the returns of 'preprocess_contract_call'
+enum PreProcessedCall {
+    Success {
+        call_args: CallArgs,
+        name_matches: Vec<usize>,
+    },
+    DefaultConstructor(Expression),
+    Error,
+}
+
+/// This functions preprocesses calls to contracts, i.e. it parses the call arguments,
+/// find function name matches and identifies if we are calling a constructor on Solana.
+fn preprocess_contract_call<T>(
+    loc: &pt::Loc,
+    call_args: &[&pt::NamedArgument],
+    external_contract_no: usize,
+    func: &pt::Identifier,
+    args: &[T],
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+) -> PreProcessedCall {
+    let call_args = if let Ok(call_args) = parse_call_args(
+        loc,
+        call_args,
+        Some(external_contract_no),
+        func.name != "new",
+        context,
+        ns,
+        symtable,
+        diagnostics,
+    ) {
+        call_args
+    } else {
+        return PreProcessedCall::Error;
+    };
+
+    let mut name_matches: Vec<usize> = Vec::new();
+
+    for function_no in ns.contracts[external_contract_no].all_functions.keys() {
+        if func.name != ns.functions[*function_no].name
+            || ns.functions[*function_no].ty != pt::FunctionTy::Function
+        {
+            continue;
+        }
+
+        name_matches.push(*function_no);
+    }
+
+    if ns.target == Target::Solana && func.name == "new" {
+        solana_constructor_check(
+            loc,
+            external_contract_no,
+            diagnostics,
+            context,
+            &call_args,
+            ns,
+        );
+
+        let constructor_nos = ns.contracts[external_contract_no].constructors(ns);
+        if !constructor_nos.is_empty() {
+            // Solana contracts shall have only a single constructor
+            assert_eq!(constructor_nos.len(), 1);
+            name_matches.push(constructor_nos[0]);
+        } else if !args.is_empty() {
+            // Default constructor must not receive arguments
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                format!(
+                    "'{}' constructor takes no argument",
+                    ns.contracts[external_contract_no].name
+                ),
+            ));
+            return PreProcessedCall::Error;
+        } else {
+            // Default constructor case
+            return PreProcessedCall::DefaultConstructor(Expression::Constructor {
+                loc: *loc,
+                contract_no: external_contract_no,
+                constructor_no: None,
+                args: vec![],
+                call_args,
+            });
+        }
+    }
+
+    PreProcessedCall::Success {
+        call_args,
+        name_matches,
+    }
+}
+
+/// This function generates the final expression when a contract's function is matched with both
+/// the provided name and arguments
+fn contract_call_match(
+    loc: &pt::Loc,
+    func: &pt::Identifier,
+    function_no: usize,
+    external_contract_no: usize,
+    call_args: CallArgs,
+    cast_args: Vec<Expression>,
+    var_expr: Option<&Expression>,
+    ns: &Namespace,
+    diagnostics: &mut Diagnostics,
+    resolve_to: ResolveTo,
+) -> Result<Expression, ()> {
+    if !ns.functions[function_no].is_public() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            format!("function '{}' is not 'public' or 'external'", func.name),
+        ));
+        return Err(());
+    } else if let Some(value) = &call_args.value {
+        if !value.const_zero(ns) && !ns.functions[function_no].is_payable() {
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                format!(
+                    "sending value to function '{}' which is not payable",
+                    func.name
+                ),
+            ));
+            return Err(());
+        }
+    }
+
+    let func = &ns.functions[function_no];
+    let returns = function_returns(func, resolve_to);
+    let ty = function_type(func, true, resolve_to);
+
+    let (address, implicit) = if let Some(program_id_var) = &call_args.program_id {
+        (*program_id_var.clone(), false)
+    } else if let Some(address_id) = &ns.contracts[external_contract_no].program_id {
+        (
+            Expression::NumberLiteral {
+                loc: *loc,
+                ty: Type::Address(false),
+                value: BigInt::from_bytes_be(Sign::Plus, address_id),
+            },
+            false,
+        )
+    } else if let Some(var) = var_expr {
+        (var.clone(), true)
+    } else {
+        unreachable!("address not found")
+    };
+
+    Ok({
+        Expression::ExternalFunctionCall {
+            loc: *loc,
+            returns,
+            function: Box::new(Expression::ExternalFunction {
+                loc: *loc,
+                ty,
+                function_no,
+                address: Box::new(address.cast(
+                    &address.loc(),
+                    &Type::Contract(func.contract_no.unwrap()),
+                    implicit,
+                    ns,
+                    diagnostics,
+                )?),
+            }),
+            args: cast_args,
+            call_args,
+        }
+    })
+}

+ 2 - 1
src/sema/expression/member_access.rs

@@ -10,6 +10,7 @@ use crate::sema::expression::function_call::function_type;
 use crate::sema::expression::integers::bigint_to_expression;
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::expression::{ExprContext, ResolveTo};
+use crate::sema::namespace::ResolveTypeContext;
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::symtable::Symtable;
 use crate::sema::unused_variable::{assigned_variable, used_variable};
@@ -648,7 +649,7 @@ fn type_name_expr(
     let ty = ns.resolve_type(
         context.file_no,
         context.contract_no,
-        false,
+        ResolveTypeContext::FunctionType,
         &args[0],
         diagnostics,
     )?;

+ 15 - 2
src/sema/functions.rs

@@ -10,6 +10,7 @@ use super::{
 };
 use crate::sema::ast::ParameterAnnotation;
 use crate::sema::function_annotation::unexpected_parameter_annotation;
+use crate::sema::namespace::ResolveTypeContext;
 use crate::Target;
 use solang_parser::pt::FunctionTy;
 use solang_parser::{
@@ -881,7 +882,13 @@ pub fn resolve_params(
 
         let mut ty_loc = p.ty.loc();
 
-        match ns.resolve_type(file_no, contract_no, false, &p.ty, diagnostics) {
+        match ns.resolve_type(
+            file_no,
+            contract_no,
+            ResolveTypeContext::None,
+            &p.ty,
+            diagnostics,
+        ) {
             Ok(ty) => {
                 if !is_internal {
                     if ty.contains_internal_function(ns) {
@@ -1002,7 +1009,13 @@ pub fn resolve_returns(
 
         let mut ty_loc = r.ty.loc();
 
-        match ns.resolve_type(file_no, contract_no, false, &r.ty, diagnostics) {
+        match ns.resolve_type(
+            file_no,
+            contract_no,
+            ResolveTypeContext::None,
+            &r.ty,
+            diagnostics,
+        ) {
             Ok(ty) => {
                 if !is_internal {
                     if ty.contains_internal_function(ns) {

+ 43 - 11
src/sema/namespace.rs

@@ -25,6 +25,14 @@ use solang_parser::{
 };
 use std::collections::HashMap;
 
+/// Provides context information for the `resolve_type` function.
+#[derive(PartialEq, Eq)]
+pub(super) enum ResolveTypeContext {
+    None,
+    Casting,
+    FunctionType,
+}
+
 impl Namespace {
     /// Create a namespace and populate with the parameters for the target
     pub fn new(target: Target) -> Self {
@@ -892,7 +900,7 @@ impl Namespace {
         &mut self,
         file_no: usize,
         contract_no: Option<usize>,
-        casting: bool,
+        resolve_context: ResolveTypeContext,
         id: &pt::Expression,
         diagnostics: &mut Diagnostics,
     ) -> Result<Type, ()> {
@@ -946,10 +954,20 @@ impl Namespace {
                     value_name,
                     ..
                 } => {
-                    let key_ty =
-                        self.resolve_type(file_no, contract_no, false, key, diagnostics)?;
-                    let value_ty =
-                        self.resolve_type(file_no, contract_no, false, value, diagnostics)?;
+                    let key_ty = self.resolve_type(
+                        file_no,
+                        contract_no,
+                        ResolveTypeContext::None,
+                        key,
+                        diagnostics,
+                    )?;
+                    let value_ty = self.resolve_type(
+                        file_no,
+                        contract_no,
+                        ResolveTypeContext::None,
+                        value,
+                        diagnostics,
+                    )?;
 
                     match key_ty {
                         Type::Mapping(..) => {
@@ -1162,7 +1180,7 @@ impl Namespace {
                     }
                 }
                 pt::Type::Payable => {
-                    if !casting {
+                    if resolve_context != ResolveTypeContext::Casting {
                         diagnostics.push(Diagnostic::decl_error(
                             id.loc(),
                             "'payable' cannot be used for type declarations, only casting. use 'address payable'"
@@ -1211,11 +1229,25 @@ impl Namespace {
                 Box::new(Type::Struct(*str_ty)),
                 resolve_dimensions(&dimensions, diagnostics)?,
             )),
-            Some(Symbol::Contract(_, n)) if dimensions.is_empty() => Ok(Type::Contract(*n)),
-            Some(Symbol::Contract(_, n)) => Ok(Type::Array(
-                Box::new(Type::Contract(*n)),
-                resolve_dimensions(&dimensions, diagnostics)?,
-            )),
+            Some(Symbol::Contract(_, n)) => {
+                if self.target == Target::Solana
+                    && resolve_context != ResolveTypeContext::FunctionType
+                {
+                    diagnostics.push(Diagnostic::error(
+                        id.loc,
+                        "contracts are not allowed as types on Solana".to_string(),
+                    ));
+                    return Err(());
+                }
+                if dimensions.is_empty() {
+                    Ok(Type::Contract(*n))
+                } else {
+                    Ok(Type::Array(
+                        Box::new(Type::Contract(*n)),
+                        resolve_dimensions(&dimensions, diagnostics)?,
+                    ))
+                }
+            }
             Some(Symbol::Event(_)) => {
                 diagnostics.push(Diagnostic::decl_error(
                     id.loc,

+ 8 - 2
src/sema/statements.rs

@@ -18,6 +18,7 @@ use crate::sema::expression::function_call::{
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::function_annotation::function_body_annotations;
 use crate::sema::function_annotation::{unexpected_parameter_annotation, UnresolvedAnnotation};
+use crate::sema::namespace::ResolveTypeContext;
 use crate::sema::symtable::{VariableInitializer, VariableUsage};
 use crate::sema::unused_variable::{assigned_variable, check_function_call, used_variable};
 use crate::sema::yul::resolve_inline_assembly;
@@ -1838,8 +1839,13 @@ fn resolve_var_decl_ty(
     diagnostics: &mut Diagnostics,
 ) -> Result<(Type, pt::Loc), ()> {
     let mut loc_ty = ty.loc();
-    let mut var_ty =
-        ns.resolve_type(context.file_no, context.contract_no, false, ty, diagnostics)?;
+    let mut var_ty = ns.resolve_type(
+        context.file_no,
+        context.contract_no,
+        ResolveTypeContext::None,
+        ty,
+        diagnostics,
+    )?;
 
     if let Some(storage) = storage {
         if !var_ty.can_have_data_location() {

+ 8 - 12
src/sema/tests/mod.rs

@@ -457,9 +457,8 @@ contract aborting {
 
 contract runner {
     function test() external pure {
-        aborting abort = new aborting();
 
-        try abort.abort() returns (int32 a, bool b) {
+        try aborting.abort() returns (int32 a, bool b) {
             // call succeeded; return values are in a and b
         }
         catch Error(string x) {
@@ -535,16 +534,15 @@ fn dynamic_account_metas() {
     import 'solana';
 
 contract creator {
-    Child public c;
     function create_child_with_meta(address child, address payer) public {
         AccountMeta[] metas = new AccountMeta[](2);
 
         metas[0] = AccountMeta({pubkey: child, is_signer: false, is_writable: false});
         metas[1] = AccountMeta({pubkey: payer, is_signer: true, is_writable: true});
 
-        c = new Child{accounts: metas}(payer);
+        Child.new{accounts: metas}(payer);
 
-        c.say_hello();
+        Child.say_hello();
     }
 }
 
@@ -580,19 +578,17 @@ fn no_address_and_no_metas() {
     import 'solana';
 
 contract creator {
-    Child public c;
-    function create_child_with_meta(address child, address payer) public {
-
-        c = new Child(payer);
-
-        c.say_hello();
+    function create_child_with_meta(address child) public {
+        Child.new();
+        Child.say_hello();
     }
 }
 
+@program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
 contract Child {
     @payer(payer)
     @space(511 + 7)
-    constructor(address payer) {
+    constructor() {
         print("In child constructor");
     }
 

+ 29 - 6
src/sema/types.rs

@@ -10,6 +10,7 @@ use super::{
     diagnostics::Diagnostics,
     ContractDefinition, SOLANA_SPARSE_ARRAY_SIZE,
 };
+use crate::sema::namespace::ResolveTypeContext;
 use crate::Target;
 use base58::{FromBase58, FromBase58Error};
 use indexmap::IndexMap;
@@ -214,7 +215,13 @@ fn type_decl(
 ) {
     let mut diagnostics = Diagnostics::default();
 
-    let mut ty = match ns.resolve_type(file_no, contract_no, false, &def.ty, &mut diagnostics) {
+    let mut ty = match ns.resolve_type(
+        file_no,
+        contract_no,
+        ResolveTypeContext::None,
+        &def.ty,
+        &mut diagnostics,
+    ) {
         Ok(ty) => ty,
         Err(_) => {
             ns.diagnostics.extend(diagnostics);
@@ -700,7 +707,13 @@ pub fn struct_decl(
     for field in &def.fields {
         let mut diagnostics = Diagnostics::default();
 
-        let ty = match ns.resolve_type(file_no, contract_no, false, &field.ty, &mut diagnostics) {
+        let ty = match ns.resolve_type(
+            file_no,
+            contract_no,
+            ResolveTypeContext::None,
+            &field.ty,
+            &mut diagnostics,
+        ) {
             Ok(s) => s,
             Err(()) => {
                 ns.diagnostics.extend(diagnostics);
@@ -795,8 +808,13 @@ fn event_decl(
     for field in &def.fields {
         let mut diagnostics = Diagnostics::default();
 
-        let mut ty = match ns.resolve_type(file_no, contract_no, false, &field.ty, &mut diagnostics)
-        {
+        let mut ty = match ns.resolve_type(
+            file_no,
+            contract_no,
+            ResolveTypeContext::None,
+            &field.ty,
+            &mut diagnostics,
+        ) {
             Ok(s) => s,
             Err(()) => {
                 ns.diagnostics.extend(diagnostics);
@@ -913,8 +931,13 @@ fn error_decl(
     for field in &def.fields {
         let mut diagnostics = Diagnostics::default();
 
-        let mut ty = match ns.resolve_type(file_no, contract_no, false, &field.ty, &mut diagnostics)
-        {
+        let mut ty = match ns.resolve_type(
+            file_no,
+            contract_no,
+            ResolveTypeContext::None,
+            &field.ty,
+            &mut diagnostics,
+        ) {
             Ok(s) => s,
             Err(()) => {
                 ns.diagnostics.extend(diagnostics);

+ 3 - 0
src/sema/unused_variable.rs

@@ -281,6 +281,9 @@ fn check_call_args(ns: &mut Namespace, call_args: &CallArgs, symtable: &mut Symt
     if let Some(flags) = &call_args.flags {
         used_variable(ns, flags.as_ref(), symtable);
     }
+    if let Some(program_id) = &call_args.program_id {
+        used_variable(ns, program_id.as_ref(), symtable);
+    }
 }
 
 /// Marks as used variables that appear in an expression with right and left hand side.

+ 8 - 1
src/sema/using.rs

@@ -10,6 +10,7 @@ use super::{
 };
 use crate::sema::expression::function_call::{function_returns, function_type};
 use crate::sema::expression::resolve_expression::expression;
+use crate::sema::namespace::ResolveTypeContext;
 use solang_parser::pt::CodeLocation;
 use solang_parser::pt::{self};
 use std::collections::HashSet;
@@ -34,7 +35,13 @@ pub(crate) fn using_decl(
     }
 
     let ty = if let Some(expr) = &using.ty {
-        match ns.resolve_type(file_no, contract_no, false, expr, &mut diagnostics) {
+        match ns.resolve_type(
+            file_no,
+            contract_no,
+            ResolveTypeContext::None,
+            expr,
+            &mut diagnostics,
+        ) {
             Ok(Type::Contract(contract_no)) if ns.contracts[contract_no].is_library() => {
                 ns.diagnostics.push(Diagnostic::error(
                     expr.loc(),

+ 8 - 1
src/sema/variables.rs

@@ -16,6 +16,7 @@ use super::{
 };
 use crate::sema::eval::check_term_for_constant_overflow;
 use crate::sema::expression::resolve_expression::expression;
+use crate::sema::namespace::ResolveTypeContext;
 use crate::sema::Recurse;
 use solang_parser::{
     doccomment::DocComment,
@@ -120,7 +121,13 @@ pub fn variable_decl<'a>(
 
     let mut diagnostics = Diagnostics::default();
 
-    let ty = match ns.resolve_type(file_no, contract_no, false, &ty, &mut diagnostics) {
+    let ty = match ns.resolve_type(
+        file_no,
+        contract_no,
+        ResolveTypeContext::None,
+        &ty,
+        &mut diagnostics,
+    ) {
         Ok(s) => s,
         Err(()) => {
             ns.diagnostics.extend(diagnostics);

+ 6 - 0
tests/codegen_testcases/import_test.sol

@@ -0,0 +1,6 @@
+@program_id("6qEm4QUJGFvqKNJGjTrAEiFhbVBY4ashpBjDHEFvEUmW")
+contract Dog {
+    function barks(string what) public pure {
+        print(what);
+    }
+}

+ 16 - 0
tests/codegen_testcases/solidity/accounts_for_default_constructor.sol

@@ -0,0 +1,16 @@
+// RUN: --target solana --emit cfg
+contract Foo {
+    uint b;
+    function get_b() public returns (uint) {
+        return b;
+    }
+}
+
+contract Other {
+    // BEGIN-CHECK: Other::Other::function::call_foo__address
+    function call_foo(address id) external {
+        // The account must be properly indexed so that the call works.
+        // CHECK: constructor(no: ) salt: value: gas:uint64 0 address:(arg #0) seeds: Foo encoded buffer: %abi_encoded.temp.12 accounts: [1] [ struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 0]) field 0)), true, false } ]
+        Foo.new{program_id: id}();
+    }
+}

+ 98 - 101
tests/codegen_testcases/solidity/borsh_decoding_simple_types.sol

@@ -1,19 +1,16 @@
 // RUN: --target solana --emit cfg
-contract Other {
-
-}
 
 contract Testing {
     // BEGIN-CHECK: Testing::Testing::function::addressContract__bytes
-    function addressContract(bytes memory buffer) public pure returns (address, Other) {
-        (address a, Other b) = abi.decode(buffer, (address, Other));
+    function addressContract(bytes memory buffer) public pure returns (address, address) {
+        (address a, address b) = abi.decode(buffer, (address, address));
 	    // CHECK: ty:bytes %buffer = (arg #0)
-	    // CHECK: ty:uint32 %temp.64 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 64 <= %temp.64), block1, block2
+	    // CHECK: ty:uint32 %temp.60 = (builtin ArrayLength ((arg #0)))
+	    // CHECK: branchcond (unsigned uint32 64 <= %temp.60), block1, block2
         // CHECK: block1: # inbounds
-        // CHECK: ty:address %temp.65 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-        // CHECK: ty:contract Other %temp.66 = (builtin ReadFromBuffer ((arg #0), uint32 32))
-        // CHECK: branchcond (unsigned less uint32 64 < %temp.64), block3, block4
+        // CHECK: ty:address %temp.61 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+        // CHECK: ty:address %temp.62 = (builtin ReadFromBuffer ((arg #0), uint32 32))
+        // CHECK: branchcond (unsigned less uint32 64 < %temp.60), block3, block4
         // CHECK: block2: # out_of_bounds
         // CHECK: assert-failure
 
@@ -21,8 +18,8 @@ contract Testing {
 	    // CHECK: assert-failure
 
         // CHECK: block4: # buffer_read
-	    // CHECK: ty:address %a = %temp.65
-	    // CHECK: ty:contract Other %b = %temp.66
+	    // CHECK: ty:address %a = %temp.61
+	    // CHECK: ty:address %b = %temp.62
         return (a, b);
     }
 
@@ -33,17 +30,17 @@ contract Testing {
         abi.decode(buffer, (uint8, uint16, uint32, uint64, uint128, uint256));
 
 	    // CHECK: ty:bytes %buffer = (arg #0)
-	    // CHECK: ty:uint32 %temp.67 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 63 <= %temp.67), block1, block2
+	    // CHECK: ty:uint32 %temp.63 = (builtin ArrayLength ((arg #0)))
+	    // CHECK: branchcond (unsigned uint32 63 <= %temp.63), block1, block2
 
         // CHECK: block1: # inbounds
-	    // CHECK: ty:uint8 %temp.68 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: ty:uint16 %temp.69 = (builtin ReadFromBuffer ((arg #0), uint32 1))
-	    // CHECK: ty:uint32 %temp.70 = (builtin ReadFromBuffer ((arg #0), uint32 3))
-	    // CHECK: ty:uint64 %temp.71 = (builtin ReadFromBuffer ((arg #0), uint32 7))
-	    // CHECK: ty:uint128 %temp.72 = (builtin ReadFromBuffer ((arg #0), uint32 15))
-	    // CHECK: ty:uint256 %temp.73 = (builtin ReadFromBuffer ((arg #0), uint32 31))
-	    // CHECK: branchcond (unsigned less uint32 63 < %temp.67), block3, block4
+	    // CHECK: ty:uint8 %temp.64 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	    // CHECK: ty:uint16 %temp.65 = (builtin ReadFromBuffer ((arg #0), uint32 1))
+	    // CHECK: ty:uint32 %temp.66 = (builtin ReadFromBuffer ((arg #0), uint32 3))
+	    // CHECK: ty:uint64 %temp.67 = (builtin ReadFromBuffer ((arg #0), uint32 7))
+	    // CHECK: ty:uint128 %temp.68 = (builtin ReadFromBuffer ((arg #0), uint32 15))
+	    // CHECK: ty:uint256 %temp.69 = (builtin ReadFromBuffer ((arg #0), uint32 31))
+	    // CHECK: branchcond (unsigned less uint32 63 < %temp.63), block3, block4
 
         // CHECK: block2: # out_of_bounds
         // CHECK: assert-failure
@@ -52,12 +49,12 @@ contract Testing {
 	    // CHECK: assert-failure
 
         // CHECK: block4: # buffer_read
-	    // CHECK: ty:uint8 %a = %temp.68
-	    // CHECK: ty:uint16 %b = %temp.69
-	    // CHECK: ty:uint32 %c = %temp.70
-	    // CHECK: ty:uint64 %d = %temp.71
-	    // CHECK: ty:uint128 %e = %temp.72
-	    // CHECK: ty:uint256 %f = %temp.73
+	    // CHECK: ty:uint8 %a = %temp.64
+	    // CHECK: ty:uint16 %b = %temp.65
+	    // CHECK: ty:uint32 %c = %temp.66
+	    // CHECK: ty:uint64 %d = %temp.67
+	    // CHECK: ty:uint128 %e = %temp.68
+	    // CHECK: ty:uint256 %f = %temp.69
 
         return (a, b, c, d, e, f);
     }
@@ -68,17 +65,17 @@ contract Testing {
         (int8 a, int16 b, int32 c, int64 d, int128 e, int256 f) =
         abi.decode(buffer, (int8, int16, int32, int64, int128, int256));
 
-        // CHECK: ty:uint32 %temp.74 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 63 <= %temp.74), block1, block2
+        // CHECK: ty:uint32 %temp.70 = (builtin ArrayLength ((arg #0)))
+	    // CHECK: branchcond (unsigned uint32 63 <= %temp.70), block1, block2
 
         // CHECK: block1: # inbounds
-	    // CHECK: ty:int8 %temp.75 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: ty:int16 %temp.76 = (builtin ReadFromBuffer ((arg #0), uint32 1))
-	    // CHECK: ty:int32 %temp.77 = (builtin ReadFromBuffer ((arg #0), uint32 3))
-	    // CHECK: ty:int64 %temp.78 = (builtin ReadFromBuffer ((arg #0), uint32 7))
-	    // CHECK: ty:int128 %temp.79 = (builtin ReadFromBuffer ((arg #0), uint32 15))
-	    // CHECK: ty:int256 %temp.80 = (builtin ReadFromBuffer ((arg #0), uint32 31))
-	    // CHECK: branchcond (unsigned less uint32 63 < %temp.74), block3, block4
+	    // CHECK: ty:int8 %temp.71 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	    // CHECK: ty:int16 %temp.72 = (builtin ReadFromBuffer ((arg #0), uint32 1))
+	    // CHECK: ty:int32 %temp.73 = (builtin ReadFromBuffer ((arg #0), uint32 3))
+	    // CHECK: ty:int64 %temp.74 = (builtin ReadFromBuffer ((arg #0), uint32 7))
+	    // CHECK: ty:int128 %temp.75 = (builtin ReadFromBuffer ((arg #0), uint32 15))
+	    // CHECK: ty:int256 %temp.76 = (builtin ReadFromBuffer ((arg #0), uint32 31))
+	    // CHECK: branchcond (unsigned less uint32 63 < %temp.70), block3, block4
 
         // CHECK: block2: # out_of_bounds
 	    // CHECK: assert-failure
@@ -87,12 +84,12 @@ contract Testing {
 	    // CHECK: assert-failure
 
         // CHECK: block4: # buffer_read
-		// CHECK: ty:int8 %a = %temp.75
-	    // CHECK: ty:int16 %b = %temp.76
-	    // CHECK: ty:int32 %c = %temp.77
-	    // CHECK: ty:int64 %d = %temp.78
-	    // CHECK: ty:int128 %e = %temp.79
-	    // CHECK: ty:int256 %f = %temp.80
+		// CHECK: ty:int8 %a = %temp.71
+	    // CHECK: ty:int16 %b = %temp.72
+	    // CHECK: ty:int32 %c = %temp.73
+	    // CHECK: ty:int64 %d = %temp.74
+	    // CHECK: ty:int128 %e = %temp.75
+	    // CHECK: ty:int256 %f = %temp.76
 
         return (a, b, c, d, e, f);
      }
@@ -101,15 +98,15 @@ contract Testing {
     function fixedBytes(bytes memory buffer) public pure returns (bytes1, bytes5, bytes20, bytes32) {
         (bytes1 a, bytes5 b, bytes20 c, bytes32 d) = abi.decode(buffer, (bytes1, bytes5, bytes20, bytes32));
 
-        // CHECK: ty:uint32 %temp.81 = (builtin ArrayLength ((arg #0)))
-	    // CHECK: branchcond (unsigned uint32 58 <= %temp.81), block1, block2
+        // CHECK: ty:uint32 %temp.77 = (builtin ArrayLength ((arg #0)))
+	    // CHECK: branchcond (unsigned uint32 58 <= %temp.77), block1, block2
 
         // CHECK: block1: # inbounds
-	    // CHECK: ty:bytes1 %temp.82 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-	    // CHECK: ty:bytes5 %temp.83 = (builtin ReadFromBuffer ((arg #0), uint32 1))
-	    // CHECK: ty:bytes20 %temp.84 = (builtin ReadFromBuffer ((arg #0), uint32 6))
-	    // CHECK: ty:bytes32 %temp.85 = (builtin ReadFromBuffer ((arg #0), uint32 26))
-	    // CHECK: branchcond (unsigned less uint32 58 < %temp.81), block3, block4
+	    // CHECK: ty:bytes1 %temp.78 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	    // CHECK: ty:bytes5 %temp.79 = (builtin ReadFromBuffer ((arg #0), uint32 1))
+	    // CHECK: ty:bytes20 %temp.80 = (builtin ReadFromBuffer ((arg #0), uint32 6))
+	    // CHECK: ty:bytes32 %temp.81 = (builtin ReadFromBuffer ((arg #0), uint32 26))
+	    // CHECK: branchcond (unsigned less uint32 58 < %temp.77), block3, block4
 
         // CHECK: block2: # out_of_bounds
 	    // CHECK: assert-failure
@@ -118,10 +115,10 @@ contract Testing {
 	    // CHECK: assert-failure
 
         // CHECK: block4: # buffer_read
-		// CHECK: ty:bytes1 %a = %temp.82
-	    // CHECK: ty:bytes5 %b = %temp.83
-	    // CHECK: ty:bytes20 %c = %temp.84
-	    // CHECK: ty:bytes32 %d = %temp.85
+		// CHECK: ty:bytes1 %a = %temp.78
+	    // CHECK: ty:bytes5 %b = %temp.79
+	    // CHECK: ty:bytes20 %c = %temp.80
+	    // CHECK: ty:bytes32 %d = %temp.81
 
         return (a, b, c, d);
     }
@@ -131,38 +128,38 @@ contract Testing {
         (bytes memory a, string memory b) = abi.decode(buffer, (bytes, string));
 
 		// CHECK: ty:bytes %buffer = (arg #0)
-		// CHECK: ty:uint32 %temp.86 = (builtin ArrayLength ((arg #0)))
-		// CHECK: ty:uint32 %temp.87 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-		// CHECK: branchcond (unsigned uint32 4 <= %temp.86), block1, block2
+		// CHECK: ty:uint32 %temp.82 = (builtin ArrayLength ((arg #0)))
+		// CHECK: ty:uint32 %temp.83 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+		// CHECK: branchcond (unsigned uint32 4 <= %temp.82), block1, block2
 
         // CHECK: block1: # inbounds
-        // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + (%temp.87 + uint32 4))
-        // CHECK: branchcond (unsigned %1.cse_temp <= %temp.86), block3, block4
+        // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + (%temp.83 + uint32 4))
+        // CHECK: branchcond (unsigned %1.cse_temp <= %temp.82), block3, block4
 
         // CHECK: block2: # out_of_bounds
         // CHECK: assert-failure
 
         // CHECK: block3: # inbounds
-        // CHECK: ty:bytes %temp.88 = (alloc bytes len %temp.87)
-        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 4), dest: %temp.88, bytes_len: %temp.87
-        // CHECK: ty:uint32 %temp.89 = (builtin ReadFromBuffer ((arg #0), (uint32 0 + (%temp.87 + uint32 4))))
+        // CHECK: ty:bytes %temp.84 = (alloc bytes len %temp.83)
+        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 4), dest: %temp.84, bytes_len: %temp.83
+        // CHECK: ty:uint32 %temp.85 = (builtin ReadFromBuffer ((arg #0), (uint32 0 + (%temp.83 + uint32 4))))
         // CHECK: ty:uint32 %2.cse_temp = (%1.cse_temp + uint32 4)
-        // CHECK: branchcond (unsigned %2.cse_temp <= %temp.86), block5, block6
+        // CHECK: branchcond (unsigned %2.cse_temp <= %temp.82), block5, block6
 
         // CHECK: block4: # out_of_bounds
         // CHECK: assert-failure
 
         // CHECK: block5: # inbounds
-        // CHECK: ty:uint32 %3.cse_temp = (%1.cse_temp + (%temp.89 + uint32 4))
-        // CHECK: branchcond (unsigned %3.cse_temp <= %temp.86), block7, block8
+        // CHECK: ty:uint32 %3.cse_temp = (%1.cse_temp + (%temp.85 + uint32 4))
+        // CHECK: branchcond (unsigned %3.cse_temp <= %temp.82), block7, block8
 
         // CHECK: block6: # out_of_bounds
         // CHECK: assert-failure
 
         // CHECK: block7: # inbounds
-        // CHECK: ty:string %temp.90 = (alloc string len %temp.89)
-        // CHECK: memcpy src: (advance ptr: %buffer, by: %2.cse_temp), dest: %temp.90, bytes_len: %temp.89
-        // CHECK: branchcond (unsigned less %3.cse_temp < %temp.86), block9, block10
+        // CHECK: ty:string %temp.86 = (alloc string len %temp.85)
+        // CHECK: memcpy src: (advance ptr: %buffer, by: %2.cse_temp), dest: %temp.86, bytes_len: %temp.85
+        // CHECK: branchcond (unsigned less %3.cse_temp < %temp.82), block9, block10
 
         // CHECK: block8: # out_of_bounds
         // CHECK: assert-failure
@@ -171,8 +168,8 @@ contract Testing {
         // CHECK: assert-failure
 
         // CHECK: block10: # buffer_read
-        // CHECK: ty:bytes %a = %temp.88
-        // CHECK: ty:string %b = %temp.90
+        // CHECK: ty:bytes %a = %temp.84
+        // CHECK: ty:string %b = %temp.86
 
         return (a, b);
     }
@@ -186,12 +183,12 @@ contract Testing {
         WeekDays a = abi.decode(buffer, (WeekDays));
 
 		// CHECK: ty:bytes %buffer = (arg #0)
-		// CHECK: ty:uint32 %temp.94 = (builtin ArrayLength ((arg #0)))
-		// CHECK: branchcond (unsigned uint32 1 <= %temp.94), block1, block2
+		// CHECK: ty:uint32 %temp.90 = (builtin ArrayLength ((arg #0)))
+		// CHECK: branchcond (unsigned uint32 1 <= %temp.90), block1, block2
 
 		// CHECK: block1: # inbounds
-		// CHECK: ty:enum Testing.WeekDays %temp.95 = (builtin ReadFromBuffer ((arg #0), uint32 0))
-		// CHECK: branchcond (unsigned less uint32 1 < %temp.94), block3, block4
+		// CHECK: ty:enum Testing.WeekDays %temp.91 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+		// CHECK: branchcond (unsigned less uint32 1 < %temp.90), block3, block4
 
 		// CHECK: block2: # out_of_bounds
 		// CHECK: assert-failure
@@ -200,7 +197,7 @@ contract Testing {
 		// CHECK: assert-failure
 
 		// CHECK: block4: # buffer_read
-		// CHECK: ty:enum Testing.WeekDays %a = %temp.95
+		// CHECK: ty:enum Testing.WeekDays %a = %temp.91
 
         return a;
     }
@@ -220,17 +217,17 @@ contract Testing {
     function decodeStruct(bytes memory buffer) public pure returns (noPadStruct memory, PaddedStruct memory) {
         (noPadStruct memory a, PaddedStruct memory b) = abi.decode(buffer, (noPadStruct, PaddedStruct));
 
-		// CHECK: ty:uint32 %temp.96 = (builtin ArrayLength ((arg #0)))
-		// CHECK: branchcond (unsigned uint32 57 <= %temp.96), block1, block2
+		// CHECK: ty:uint32 %temp.92 = (builtin ArrayLength ((arg #0)))
+		// CHECK: branchcond (unsigned uint32 57 <= %temp.92), block1, block2
 
 		// CHECK: block1: # inbounds
-		// CHECK: ty:struct Testing.noPadStruct %temp.97 = struct {  }
-		// CHECK: memcpy src: %buffer, dest: %temp.97, bytes_len: uint32 8
-        // CHECK: ty:uint128 %temp.98 = (builtin ReadFromBuffer ((arg #0), uint32 8))
-        // CHECK: ty:uint8 %temp.99 = (builtin ReadFromBuffer ((arg #0), uint32 24))
-        // CHECK: ty:bytes32 %temp.100 = (builtin ReadFromBuffer ((arg #0), uint32 25))
-        // CHECK: ty:struct Testing.PaddedStruct %temp.101 = struct { %temp.98, %temp.99, %temp.100 }
-        // CHECK: branchcond (unsigned less uint32 57 < %temp.96), block3, block4
+		// CHECK: ty:struct Testing.noPadStruct %temp.93 = struct {  }
+		// CHECK: memcpy src: %buffer, dest: %temp.93, bytes_len: uint32 8
+        // CHECK: ty:uint128 %temp.94 = (builtin ReadFromBuffer ((arg #0), uint32 8))
+        // CHECK: ty:uint8 %temp.95 = (builtin ReadFromBuffer ((arg #0), uint32 24))
+        // CHECK: ty:bytes32 %temp.96 = (builtin ReadFromBuffer ((arg #0), uint32 25))
+        // CHECK: ty:struct Testing.PaddedStruct %temp.97 = struct { %temp.94, %temp.95, %temp.96 }
+        // CHECK: branchcond (unsigned less uint32 57 < %temp.92), block3, block4
 		
 		// CHECK: block2: # out_of_bounds
 		// CHECK: assert-failure
@@ -239,8 +236,8 @@ contract Testing {
 		// CHECK: assert-failure
 
 		// CHECK: block4: # buffer_read
-		// CHECK: ty:struct Testing.noPadStruct %a = %temp.97
-		// CHECK: ty:struct Testing.PaddedStruct %b = %temp.101
+		// CHECK: ty:struct Testing.noPadStruct %a = %temp.93
+		// CHECK: ty:struct Testing.PaddedStruct %b = %temp.97
 
         return (a, b);
     }
@@ -250,31 +247,31 @@ contract Testing {
         (uint32[4] memory a, noPadStruct[2] memory b, noPadStruct[] memory c) =
         abi.decode(buffer, (uint32[4], noPadStruct[2], noPadStruct[]));
 
-		// CHECK: ty:uint32 %temp.102 = (builtin ArrayLength ((arg #0)))
-        // CHECK: branchcond (unsigned uint32 32 <= %temp.102), block1, block2
+		// CHECK: ty:uint32 %temp.98 = (builtin ArrayLength ((arg #0)))
+        // CHECK: branchcond (unsigned uint32 32 <= %temp.98), block1, block2
 
 		// CHECK: block1: # inbounds
-        // CHECK: ty:uint32[4] %temp.103 =  [  ]
-        // CHECK: memcpy src: %buffer, dest: %temp.103, bytes_len: uint32 16
-        // CHECK: ty:struct Testing.noPadStruct[2] %temp.104 =  [  ]
-        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 16), dest: %temp.104, bytes_len: uint32 16
-        // CHECK: ty:uint32 %temp.105 = (builtin ReadFromBuffer ((arg #0), uint32 32))
-        // CHECK: branchcond (unsigned uint32 36 <= %temp.102), block3, block4
+        // CHECK: ty:uint32[4] %temp.99 =  [  ]
+        // CHECK: memcpy src: %buffer, dest: %temp.99, bytes_len: uint32 16
+        // CHECK: ty:struct Testing.noPadStruct[2] %temp.100 =  [  ]
+        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 16), dest: %temp.100, bytes_len: uint32 16
+        // CHECK: ty:uint32 %temp.101 = (builtin ReadFromBuffer ((arg #0), uint32 32))
+        // CHECK: branchcond (unsigned uint32 36 <= %temp.98), block3, block4
 		
 		// CHECK: block2: # out_of_bounds
         // CHECK: assert-failure
 
 		// CHECK: block3: # inbounds
-        // CHECK: ty:struct Testing.noPadStruct[] %temp.106 = (alloc struct Testing.noPadStruct[] len %temp.105)
-        // CHECK: ty:uint32 %1.cse_temp = (%temp.105 * uint32 8)
-        // CHECK: branchcond (unsigned (uint32 36 + %1.cse_temp) <= %temp.102), block5, block6
+        // CHECK: ty:struct Testing.noPadStruct[] %temp.102 = (alloc struct Testing.noPadStruct[] len %temp.101)
+        // CHECK: ty:uint32 %1.cse_temp = (%temp.101 * uint32 8)
+        // CHECK: branchcond (unsigned (uint32 36 + %1.cse_temp) <= %temp.98), block5, block6
 
 		// CHECK: block4: # out_of_bounds
         // CHECK: assert-failure
 
 		// CHECK: block5: # inbounds
-        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 36), dest: %temp.106, bytes_len: %1.cse_temp
-        // CHECK: branchcond (unsigned less (uint32 32 + (%1.cse_temp + uint32 4)) < %temp.102), block7, block8
+        // CHECK: memcpy src: (advance ptr: %buffer, by: uint32 36), dest: %temp.102, bytes_len: %1.cse_temp
+        // CHECK: branchcond (unsigned less (uint32 32 + (%1.cse_temp + uint32 4)) < %temp.98), block7, block8
 
 		// CHECK: block6: # out_of_bounds
         // CHECK: assert-failure
@@ -283,9 +280,9 @@ contract Testing {
         // CHECK: assert-failure
 
 		// CHECK: block8: # buffer_read
-        // CHECK: ty:uint32[4] %a = %temp.103
-        // CHECK: ty:struct Testing.noPadStruct[2] %b = %temp.104
-        // CHECK: ty:struct Testing.noPadStruct[] %c = %temp.106
+        // CHECK: ty:uint32[4] %a = %temp.99
+        // CHECK: ty:struct Testing.noPadStruct[2] %b = %temp.100
+        // CHECK: ty:struct Testing.noPadStruct[] %c = %temp.102
 
         return (a, b, c);
     }

+ 3 - 4
tests/codegen_testcases/solidity/constructor_with_metas.sol

@@ -3,17 +3,16 @@
 import 'solana';
 
 contract creator {
-    Child public c;
     // BEGIN-CHECK: creator::creator::function::create_child_with_meta__address_address
     function create_child_with_meta(address child, address payer) external {
         AccountMeta[2] metas = [
             AccountMeta({pubkey: child, is_signer: false, is_writable: false}),
             AccountMeta({pubkey: payer, is_signer: true, is_writable: true})
         ];
-        // CHECK: constructor(no: 4) salt: value: gas:uint64 0 address:address 0xadde28d6c5697771bb24a668136224c7aac8e8ba974c2881484973b2e762fb74 seeds: Child encoded buffer: %abi_encoded.temp.15 accounts: %metas
-        c = new Child{accounts: metas}();
+        // CHECK: external call::regular address:address 0xadde28d6c5697771bb24a668136224c7aac8e8ba974c2881484973b2e762fb74 payload:%abi_encoded.temp.13 value:uint64 0 gas:uint64 0 accounts:%metas seeds: contract|function:(1, 3) flags:
+        Child.new{accounts: metas}();
 
-        c.say_hello();
+        Child.say_hello();
     }
 }
 

+ 25 - 0
tests/codegen_testcases/solidity/import_ext_call.sol

@@ -0,0 +1,25 @@
+// RUN: --target solana --emit cfg
+import '../import_test.sol' as My;
+
+@program_id("6qEm4QUJGFvqKNJGjTrAEiFhbVBY4ashpBjDHEFvEUmW")
+contract Foo {
+    // BEGIN-CHECK: Foo::Foo::function::get_b
+    function get_b(address id) public pure {
+        // External calls
+        // CHECK: external call::regular address:(arg #0) payload:%abi_encoded.temp.2 value:uint64 0 gas:uint64 0 accounts:[0] [  ] seeds: contract|function:(2, 2) flags:
+        My.Dog.barks{program_id: id}("woof");
+        // CHECK: external call::regular address:(arg #0) payload:%abi_encoded.temp.4 value:uint64 0 gas:uint64 0 accounts:[0] [  ] seeds: contract|function:(2, 2) flags:
+        My.Dog.barks{program_id: id}({what: "meow"});
+    }
+}
+
+contract Cat is My.Dog {
+    // BEGIN-CHECK: Cat::Cat::function::try_cat
+    function try_cat() public pure {
+        // Internal calls
+        My.Dog.barks("woof");
+        // CHECK: Cat::Dog::function::barks__string (alloc string uint32 4 "woof")
+        My.Dog.barks({what: "meow"});
+        // CHECK: Cat::Dog::function::barks__string (alloc string uint32 4 "meow")
+    }
+}

+ 46 - 0
tests/codegen_testcases/solidity/solana_base_versus_external.sol

@@ -0,0 +1,46 @@
+// RUN: --target solana --emit cfg
+
+import 'solana';
+
+@program_id("6qEm4QUJGFvqKNJGjTrAEiFhbVBY4ashpBjDHEFvEUmW")
+contract Foo {
+    uint b;
+    constructor(uint a) {
+        b = a;
+    }
+
+    function get_b(address id) public returns (uint) {
+        return b;
+    }
+
+    function get_b2(address id) public returns (uint) {
+        return b;
+    }
+}
+
+@program_id("9VLAw4to9KsX9DvyzJUJwfUeQCouX79szENj78sZKiqA")
+contract Other is Foo {
+    uint c;
+    constructor(uint d) Foo(d) {
+        c = d;
+    }
+    // BEGIN-CHECK: Other::Other::function::call_foo__address
+    function call_foo(address id) external {
+        // internal calls
+        Foo.get_b(id);
+        // CHECK: call Other::Foo::function::get_b__address (arg #0)
+        Foo.get_b2({id: id});
+        // CHECK: call Other::Foo::function::get_b2__address (arg #0)
+    }
+    // BEGIN-CHECK: Other::Other::function::call_foo2__address_address
+    function call_foo2(address id, address acc) external {
+        AccountMeta[1] meta = [
+            AccountMeta({pubkey: acc, is_writable: false, is_signer: false})
+        ];
+        // external calls
+        Foo.get_b{program_id: id, accounts: meta}(id);
+        // CHECK: external call::regular address:(arg #0) payload:%abi_encoded.temp.35 value:uint64 0 gas:uint64 0 accounts:%meta seeds: contract|function:(0, 3) flags:
+        Foo.get_b2{program_id: id, accounts: meta}(id);
+        // CHECK: external call::regular address:(arg #0) payload:%abi_encoded.temp.36 value:uint64 0 gas:uint64 0 accounts:%meta seeds: contract|function:(0, 4) flags:
+    }
+}

+ 3 - 4
tests/codegen_testcases/solidity/solana_payer_account.sol

@@ -2,15 +2,14 @@
 
 @program_id("SoLDxXQ9GMoa15i4NavZc61XGkas2aom4aNiWT6KUER")
 contract Builder {
-    Built other;
     // BEGIN-CHECK: Builder::Builder::function::build_this
     function build_this() external {
-        // CHECK: constructor(no: 4) salt: value: gas:uint64 0 address:address 0x69be884fd55a2306354c305323cc6b7ce91768be33d32a021155ef608806bcb seeds: Built encoded buffer: %abi_encoded.temp.18 accounts: [3] [ struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 2]) field 0)), true, false }, struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 1]) field 0)), true, true }, struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 4]) field 0)), false, false } ]
-        other = new Built("my_seed");
+        // CHECK: external call::regular address:address 0x69be884fd55a2306354c305323cc6b7ce91768be33d32a021155ef608806bcb payload:%abi_encoded.temp.17 value:uint64 0 gas:uint64 0 accounts:[3] [ struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 3]) field 0)), true, false }, struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 2]) field 0)), true, true }, struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 0]) field 0)), false, false } ] seeds: contract|function:(1, 4) flags:
+        Built.new("my_seed");
     }
 
     function call_that() public view {
-        other.say_this("Hold up! I'm calling!");
+        Built.say_this("Hold up! I'm calling!");
     }
 }
 

+ 14 - 0
tests/contract_testcases/polkadot/contracts/program_id.sol

@@ -0,0 +1,14 @@
+contract Foo {
+    function tryFoo() public {}
+}
+
+contract Bar {
+    Foo f;
+    function foobar(address id) public {
+        f = new Foo();
+        f.tryFoo{program_id: id}();
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 9:18-32: 'program_id' not permitted for external calls or constructors on Polkadot

+ 2 - 2
tests/contract_testcases/solana/abstract_interface.sol

@@ -2,8 +2,8 @@ abstract contract A {
 	function v(int) public virtual;
 }
 contract C {
-	function t(A a) public {
-		a.v(1);
+	function t(address id) public {
+		A.v{program_id: id}(1);
 	}
 }
 

+ 16 - 18
tests/contract_testcases/solana/accounts/constructor_in_loop.sol

@@ -1,16 +1,14 @@
 contract foo {
-    X a;
-    Y ay;
 
     function contruct() external {
-        a = new X({varia: 2, varb: 5});
+        X.new({varia: 2, varb: 5});
     }
 
 	function test1() external returns (uint) {
         // This is allowed
-        uint j = a.vara(1, 2);
+        uint j = X.vara(1, 2);
 		for (uint i = 0; i < 64; i++) {
-			j += a.vara(i, j);
+			j += X.vara(i, j);
 		}
         return j;
 	}
@@ -18,7 +16,7 @@ contract foo {
     function test2() external returns (uint) {
         uint j = 3;
 		for (uint i = 0; i < 64; i++) {
-			a = new X(i, j);
+			X.new(i, j);
 		}
         return j;
 	}
@@ -29,19 +27,19 @@ contract foo {
 		for (uint i = 0; i < 64; i++) {
 			n += i;
 		}
-        a = new X(n, j);
+        X.new(n, j);
         return j;
 	}
 
     function test4(uint v1, string v2) external {
-        ay = new Y({b: v2, a: v1});
+        Y.new({b: v2, a: v1});
     }
 
     function test5() external returns (uint) {
         uint j = 3;
         uint i=0;
 		while (i < 64) {
-			a = new X(i, j);
+			X.new(i, j);
             i++;
 		}
         return j;
@@ -53,7 +51,7 @@ contract foo {
 		while (i < 64) {
             i++;
 		}
-        a = new X(i, j);
+        X.new(i, j);
         return j;
 	}
 
@@ -61,7 +59,7 @@ contract foo {
         uint j = 3;
         uint i=0;
 		do {
-			a = new X(i, j);
+			X.new(i, j);
             i++;
 		} while (i < 64);
         return j;
@@ -73,7 +71,7 @@ contract foo {
 		do {
             i++;
 		} while (i < 64);
-        a = new X(i, j);
+        X.new(i, j);
         return j;
     }
 
@@ -85,7 +83,7 @@ contract foo {
             for(uint i=0; i<80; i++) {
                 n +=i;
             }
-            a = new X(j, n);
+            X.new(j, n);
         }
 
         return j;
@@ -116,8 +114,8 @@ contract Y {
 }
 
 // ---- Expect: diagnostics ----
-// error: 21:8-19: the {accounts: ..} call argument is needed since the constructor may be called multiple times
-// error: 37:14-35: in order to instantiate contract 'Y', a @program_id is required on contract 'Y'
-// error: 44:8-19: the {accounts: ..} call argument is needed since the constructor may be called multiple times
-// error: 64:8-19: the {accounts: ..} call argument is needed since the constructor may be called multiple times
-// error: 88:17-28: the {accounts: ..} call argument is needed since the constructor may be called multiple times
+// error: 19:4-15: the {accounts: ..} call argument is needed since the constructor may be called multiple times
+// error: 35:9-30: a contract needs a program id to be called. Either a '@program_id' must be declared above a contract or the {program_id: ...} call argument must be present
+// error: 42:4-15: the {accounts: ..} call argument is needed since the constructor may be called multiple times
+// error: 62:4-15: the {accounts: ..} call argument is needed since the constructor may be called multiple times
+// error: 86:13-24: the {accounts: ..} call argument is needed since the constructor may be called multiple times

+ 14 - 17
tests/contract_testcases/solana/accounts/double_calls.sol

@@ -1,22 +1,21 @@
 contract adult {
-    hatchling hh;
     function test() external {
-        hatchling h1 = new hatchling("luna");
-        hatchling h2 = new hatchling("sol");
+        hatchling.new("luna");
+        hatchling.new("sol");
     }
 
     function create(string id) external {
-        hh = new hatchling(id);
+        hatchling.new(id);
     }
 
     function call2() external {
-        hh.call_me("id");
-        hh.call_me("not_id");
+        hatchling.call_me("id");
+        hatchling.call_me("not_id");
     }
 
     function create2(string id2) external {
-        hh = new hatchling(id2);
-        hh.call_me(id2);
+        hatchling.new(id2);
+        hatchling.call_me(id2);
     }
 }
 
@@ -40,12 +39,10 @@ contract hatchling {
 }
 
 // ---- Expect: diagnostics ----
-// warning: 4:19-21: local variable 'h1' is unused
-// warning: 5:19-21: local variable 'h2' is unused
-// error: 5:24-44: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
-// 	note 4:24-45: other call
-// warning: 12:5-30: function can be declared 'view'
-// error: 14:9-29: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
-// 	note 13:9-25: other call
-// error: 19:9-24: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
-// 	note 18:14-32: other call
+// error: 4:9-29: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
+// 	note 3:9-30: other call
+// warning: 11:5-30: function can be declared 'view'
+// error: 13:9-36: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
+// 	note 12:9-32: other call
+// error: 18:9-31: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
+// 	note 17:9-27: other call

+ 3 - 5
tests/contract_testcases/solana/annotations/account_name_collision.sol

@@ -1,10 +1,9 @@
 @program_id("SoLDxXQ9GMoa15i4NavZc61XGkas2aom4aNiWT6KUER")
 contract Builder {
-    BeingBuilt other;
     
     @payer(payer_account)
     constructor() {
-        other = new BeingBuilt("abc");
+        BeingBuilt.new("abc");
     }
 }
 
@@ -21,6 +20,5 @@ contract BeingBuilt {
 }
 
 // ---- Expect: diagnostics ----
-// warning: 3:5-21: storage variable 'other' has been assigned, but never read
-// error: 5:5-26: account name collision encountered. Calling a function that requires an account whose name is also defined in the current function will create duplicate names in the IDL. Please, rename one of the accounts
-// 	note 15:5-26: other declaration
+// error: 4:5-26: account name collision encountered. Calling a function that requires an account whose name is also defined in the current function will create duplicate names in the IDL. Please, rename one of the accounts
+// 	note 14:5-26: other declaration

+ 6 - 8
tests/contract_testcases/solana/annotations/constructor_external_function.sol

@@ -6,27 +6,25 @@ contract Foo {
 }
 
 contract Bar {
-    Foo public foo;
-
     function external_create_foo(address addr) external {
         // This not is allowed
-        foo = new Foo{address: addr}();
+        Foo.new{address: addr}();
     }
 
     function create_foo() public {
         // This is not allowed
-        foo = new Foo();
+        Foo.new();
     }
 
     function this_is_allowed() external {
-        foo = new Foo();
+        Foo.new();
     }
 
     function call_foo() public pure {
-        foo.say_hello();
+        Foo.say_hello();
     }
 }
 
 // ---- Expect: diagnostics ----
-// error: 13:23-36: 'address' not a valid call parameter
-// error: 18:15-24: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
+// error: 11:17-30: 'address' not a valid call parameter
+// error: 16:9-18: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external

+ 9 - 9
tests/contract_testcases/solana/call/call_args_three_ways.sol

@@ -1,15 +1,15 @@
 contract C {
 	function f() public {
 		// Three different parse tree for callargs with new
-		D d = (new D{value: 1})();
-		D dd = (new D){value: 1}();
-		D ddd = new D{value: 1}();
+		(D.new{value: 1})();
+		(D.new){value: 1}();
+		D.new{value: 1}();
 	}
-	function g(D d) public {
+	function g() public {
 		// Three different parse tree for callargs
-		d.func{value: 1}();
-		(d.func){value: 1}();
-		(d.func{value: 1})();
+		D.func{value: 1}();
+		(D.func){value: 1}();
+		(D.func{value: 1})();
 	}
 }
 
@@ -20,8 +20,8 @@ contract D {
 }
 
 // ---- Expect: diagnostics ----
-// error: 4:9-28: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
-// error: 4:16-24: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer
+// error: 4:3-22: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
+// error: 4:10-18: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer
 // error: 10:10-18: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer
 // error: 11:12-20: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer
 // error: 12:11-19: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer

+ 1 - 2
tests/contract_testcases/solana/constant/not_constant.sol

@@ -11,5 +11,4 @@
         }
         
 // ---- Expect: diagnostics ----
-// error: 8:26-27: 'C' is a contract
-// error: 8:26-36: function calls via contract name are only valid for base contracts
+// error: 8:26-36: a contract needs a program id to be called. Either a '@program_id' must be declared above a contract or the {program_id: ...} call argument must be present

+ 23 - 0
tests/contract_testcases/solana/contracts/abstract_constructor.sol

@@ -0,0 +1,23 @@
+abstract contract Foo2 {
+    uint b;
+    constructor(uint a) {
+        b = a;
+    }
+}
+
+contract Other2 {
+    uint c;
+    constructor(uint d) {
+        c = d;
+    }
+    function call_foo(address id) external {
+        Foo2.new{program_id: id}(5);
+    }
+    function call_foo2(address id) external {
+        Foo2.new{program_id: id}({a: 5});
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 14:9-36: cannot construct 'Foo2' of type 'abstract contract'
+// error: 17:9-41: cannot construct 'Foo2' of type 'abstract contract'

+ 48 - 0
tests/contract_testcases/solana/contracts/circular_reference.sol

@@ -0,0 +1,48 @@
+contract Foo {
+    uint b;
+    function get_b(address id) external returns (uint) {
+        Other.new{program_id: id}();
+        return b;
+    }
+}
+
+contract Other {
+    function call_foo(address id) external {
+        Foo.new{program_id: id}();
+    }
+}
+
+contract Foo2 {
+    uint b;
+    constructor(uint a) {
+        b = a;
+    }
+
+    function get_b(address id) external returns (uint) {
+        Other2.new{program_id: id}(2);
+        return b;
+    }
+
+    function get_b2(address id) external returns (uint) {
+        Other2.new{program_id: id}({d: 2});
+        return b;
+    }
+}
+
+contract Other2 {
+    uint c;
+    constructor(uint d) {
+        c = d;
+    }
+    function call_foo(address id) external {
+        Foo2.new{program_id: id}(5);
+    }
+    function call_foo2(address id) external {
+        Foo2.new{program_id: id}({a: 5});
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 11:9-34: circular reference creating contract 'Foo'
+// error: 38:9-36: circular reference creating contract 'Foo2'
+// error: 41:9-41: circular reference creating contract 'Foo2'

+ 29 - 0
tests/contract_testcases/solana/contracts/constructor_freestanding.sol

@@ -0,0 +1,29 @@
+import 'solana';
+
+function standalone(address dataAccount) returns (address) {
+    AccountMeta[1] meta = [
+        AccountMeta(dataAccount, false, false)
+    ];
+
+    hatchling.new{accounts: meta}("my_id", dataAccount);
+    return hatchling.root{accounts: meta}();
+}
+
+@program_id("5afzkvPkrshqu4onwBCsJccb1swrt4JdAjnpzK8N4BzZ")
+contract hatchling {
+    string name;
+    address private origin;
+
+    constructor(string id, address parent) {
+        require(id != "", "name must be provided");
+        name = id;
+        origin = parent;
+    }
+
+    function root() public returns (address) {
+        return origin;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 8:5-56: constructors not allowed in free standing functions

+ 51 - 0
tests/contract_testcases/solana/contracts/contract_as_variables.sol

@@ -0,0 +1,51 @@
+import "solana";
+
+contract B {
+    starter ss;
+    function declare_contract(address addr) external returns (bool) {
+        AccountMeta[1] meta = [
+            AccountMeta({pubkey: addr, is_signer: true, is_writable: true})
+        ];
+        starter g = new starter{accounts: meta}();
+        return g.get{accounts: meta}();
+    }
+
+    function receive_contract(starter g) public {
+        g.flip();
+    }
+
+    function return_contract() external returns (starter) {
+        starter c = new starter();
+        return c;
+    }
+}
+
+
+
+@program_id("CU8sqXecq7pxweQnJq6CARonEApGN2DXcv9ukRBRgnRf")
+contract starter {
+    bool private value = true;
+
+    modifier test_modifier() {
+        print("modifier");
+        _;
+    }
+
+    constructor() {
+        print("Hello, World!");
+    }
+
+    function flip() public test_modifier {
+            value = !value;
+    }
+
+    function get() public view returns (bool) {
+            return value;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 4:5-12: contracts are not allowed as types on Solana
+// error: 9:9-16: contracts are not allowed as types on Solana
+// error: 13:31-38: contracts are not allowed as types on Solana
+// error: 17:50-57: contracts are not allowed as types on Solana

+ 28 - 0
tests/contract_testcases/solana/contracts/contract_call_freestanding.sol

@@ -0,0 +1,28 @@
+import 'solana';
+
+function standalone(address dataAccount) returns (address) {
+    AccountMeta[1] meta = [
+        AccountMeta(dataAccount, false, false)
+    ];
+    return hatchling.root{accounts: meta}();
+}
+
+@program_id("5afzkvPkrshqu4onwBCsJccb1swrt4JdAjnpzK8N4BzZ")
+contract hatchling {
+    string name;
+    address private origin;
+
+    constructor(string id, address parent) {
+        require(id != "", "name must be provided");
+        name = id;
+        origin = parent;
+    }
+
+    function root() public returns (address) {
+        return origin;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// warning: 12:5-16: storage variable 'name' has been assigned, but never read
+// warning: 21:5-45: function can be declared 'view'

+ 2 - 2
tests/contract_testcases/solana/create_contract/syntax.sol

@@ -1,10 +1,10 @@
 
         contract y {
             function f() public {
-                x a = new x{gas: 102}();
+                x.new{gas: 102}();
             }
         }
         contract x {}
     
 // ---- Expect: diagnostics ----
-// error: 4:29-37: 'gas' not permitted for external calls or constructors on Solana
+// error: 4:23-31: 'gas' not permitted for external calls or constructors on Solana

+ 2 - 2
tests/contract_testcases/solana/create_contract/syntax_01.sol

@@ -1,10 +1,10 @@
 
         contract y {
             function f() public {
-                x a = new x{salt: 102}();
+                x.new{salt: 102}();
             }
         }
         contract x {}
     
 // ---- Expect: diagnostics ----
-// error: 4:29-38: 'salt' not permitted for external calls or constructors on Solana
+// error: 4:23-32: 'salt' not permitted for external calls or constructors on Solana

+ 1 - 1
tests/contract_testcases/solana/destructure_assign_struct_member_2.sol

@@ -19,7 +19,7 @@ contract Contract {
         // get shares and eth required for each share
         Struct1[] memory struct_1 = new Struct1[](size);
 
-        (struct_1[0].a, struct_1[0].b,) = IUniswapV2Pair(_tokens[0]).getReserves();
+        (struct_1[0].a, struct_1[0].b,) = IUniswapV2Pair.getReserves{program_id: _tokens[0]}();
 
     }
 }

+ 2 - 3
tests/contract_testcases/solana/error.sol

@@ -2,12 +2,11 @@
 contract error {
 	error X();
 
-	function foo(error x) public {
+	function foo() public {
 		
 	}
 }
 
 // ---- Expect: diagnostics ----
 // warning: 3:8-9: error 'X' has never been used
-// warning: 5:2-30: function can be declared 'pure'
-// warning: 5:21-22: function parameter 'x' is unused
+// warning: 5:2-23: function can be declared 'pure'

+ 0 - 19
tests/contract_testcases/solana/expressions/contract_compare.sol

@@ -1,19 +0,0 @@
-
-contract c {
-	function cmp(d left, d right) public returns (bool) {
-		return left < right;
-	}
-
-	function cmp(d left, e right) public returns (bool) {
-		return left > right;
-	}
-
-}
-
-contract d {}
-contract e {}
-
-// ---- Expect: diagnostics ----
-// error: 7:2-53: function 'cmp' overrides function in same contract
-// 	note 3:2-53: previous definition of 'cmp'
-// error: 8:10-14: expression of type contract d not allowed

+ 3 - 4
tests/contract_testcases/solana/expressions/contract_no_init.sol

@@ -5,14 +5,13 @@
 
         contract testing {
             function test(int x) public returns (int) {
-                other o;
                 do {
                     x--;
-                    o = new other();
+                    other.new();
                 }while(x > 0);
 
-                return o.a();
+                return other.a();
             }
         }
 // ---- Expect: diagnostics ----
-// error: 11:25-36: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
+// error: 10:21-32: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external

+ 3 - 3
tests/contract_testcases/solana/expressions/selector_in_free_function_02.sol

@@ -4,11 +4,11 @@
         }
 
         contract foo {
-            function f(I t) public returns (bytes8) {
-                return t.X.selector;
+            function f() public returns (bytes8) {
+                return I.X.selector;
             }
         }
         
 // ---- Expect: diagnostics ----
 // warning: 3:13-34: function can be declared 'pure'
-// warning: 7:13-52: function can be declared 'pure'
+// warning: 7:13-49: function can be declared 'pure'

+ 3 - 2
tests/contract_testcases/solana/functions/external_functions.sol

@@ -1,7 +1,7 @@
 
-function doThis(bar1 bb) returns (int) {
+function doThis(address id) returns (int) {
     // This is allwoed
-    return bb.this_is_external(1, 2);
+    return bar1.this_is_external{program_id: id}(1, 2);
 }
 
 contract bar1 {
@@ -46,5 +46,6 @@ contract bar2 is bar1 {
 // 	note 19:5-61: declaration of function 'hello'
 // error: 30:16-35: functions declared external cannot be called via an internal function call
 // 	note 19:5-61: declaration of function 'hello'
+// error: 35:16-43: a contract needs a program id to be called. Either a '@program_id' must be declared above a contract or the {program_id: ...} call argument must be present
 // error: 40:16-38: functions declared external cannot be called via an internal function call
 // 	note 10:5-72: declaration of function 'this_is_external'

+ 6 - 5
tests/contract_testcases/solana/garbage_function_args.sol

@@ -1,3 +1,4 @@
+@program_id("A8A3VYtDN69E72gceahcfVjLbf7m3c1u2RDwnbWgfRAk")
 contract c {
 	function g(address) public {
 		require(rubbish, "rubbish n'existe pas!");
@@ -17,8 +18,8 @@ contract c {
 }
 
 // ---- Expect: diagnostics ----
-// error: 3:11-18: 'rubbish' not found
-// error: 6:15-18: 'meh' not found
-// error: 9:9-12: 'foo' not found
-// error: 12:10-12: 'oo' not found
-// error: 15:14-17: 'foo' not found
+// error: 4:11-18: 'rubbish' not found
+// error: 7:15-18: 'meh' not found
+// error: 10:9-12: 'foo' not found
+// error: 13:10-12: 'oo' not found
+// error: 16:14-17: 'foo' not found

+ 5 - 5
tests/contract_testcases/solana/mapping_deletion.sol

@@ -16,14 +16,14 @@ contract DeleteTest {
     }
 
     mapping(uint => data_struct) example;
-    mapping(uint => savedTest) example2;
+    mapping(uint => address) example2;
 
-    function addData() public  {
+    function addData(address pid) public  {
         data_struct dt = data_struct({addr1: address(this), addr2: tx.accounts.dataAccount.key});
         uint id = 1;
         example[id] = dt;
-        savedTest tt = new savedTest(4);
-        example2[id] = tt;
+        savedTest.new{program_id: pid}(4);
+        example2[id] = pid;
     }
 
     function deltest() external {
@@ -39,4 +39,4 @@ contract DeleteTest {
 
 }
 // ---- Expect: diagnostics ----
-// error: 25:24-40: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
+// error: 25:9-42: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external

+ 2 - 2
tests/contract_testcases/solana/type_decl_import.sol

@@ -1,11 +1,11 @@
 import "./type_decl.sol" as IMP;
 
 contract d {
-	function f(IMP.x c) public {
+	function f(address pid) public {
 		IMP.Addr a = IMP.Addr.wrap(payable(this));
 		IMP.x.Binary b = IMP.x.Binary.wrap(false);
 
-		c.f(a, b);
+		IMP.x.f{program_id: pid}(a, b);
 	}
 }
 

+ 0 - 9
tests/optimization_testcases/calls/54e619457eea3be2790d0fa66cba06d2e9a36f17.json

@@ -1,9 +0,0 @@
-{
-  "constructor": [],
-  "function": [
-    [
-      "test",
-      []
-    ]
-  ]
-}

+ 0 - 21
tests/optimization_testcases/programs/54e619457eea3be2790d0fa66cba06d2e9a36f17.sol

@@ -1,21 +0,0 @@
-interface I {
-    function f(int) external;
-}
-
-library L {
-    function F(I i, bool b, int n) public {
-        if (b) {
-            print("Hello");
-        }
-    }
-}
-
-contract C {
-    using L for I;
-
-    function test() public {
-        I i = I(address(0));
-
-        i.F(true, 102);
-    }
-}

+ 2 - 2
tests/solana_tests/abi_decode.rs

@@ -110,10 +110,10 @@ fn decode_address() {
         r#"
     contract Testing {
         function testAddress(bytes memory buffer) public view {
-            (address a, Testing b) = abi.decode(buffer, (address, Testing));
+            (address a, address b) = abi.decode(buffer, (address, address));
 
             assert(a == address(this));
-            assert(b == this);
+            assert(b == address(this));
         }
     }
         "#,

+ 3 - 2
tests/solana_tests/abi_encode.rs

@@ -1004,8 +1004,8 @@ contract caller {
         return b + 3;
     }
 
-    function do_call() view public returns (int64, int32) {
-        return (this.doThis(5), this.doThat(3));
+    function do_call(address pid) view public returns (int64, int32) {
+        return (this.doThis{program_id: pid}(5), this.doThat{program_id: pid}(3));
     }
 }"#,
     );
@@ -1018,6 +1018,7 @@ contract caller {
     let caller_program_id = vm.stack[0].id;
     let returns = vm
         .function("do_call")
+        .arguments(&[BorshToken::Address(caller_program_id)])
         .accounts(vec![
             ("systemProgram", [0; 32]),
             ("caller_programId", caller_program_id),

+ 8 - 5
tests/solana_tests/accessor.rs

@@ -262,17 +262,17 @@ fn struct_accessor() {
                 m[1023413412] = S({f1: 414243, f2: true, f3: E("niff")});
             }
 
-            function f() public view {
+            function f(address pid) public view {
                 AccountMeta[1] meta = [
                     AccountMeta({pubkey: tx.accounts.dataAccount.key, is_writable: false, is_signer: false})
                 ];
-                (int64 a1, bool b, E memory c) = this.a{accounts: meta}();
+                (int64 a1, bool b, E memory c) = this.a{accounts: meta, program_id: pid}();
                 require(a1 == -63 && !b && c.b4 == "nuff", "a");
-                (a1, b, c) = this.s{accounts: meta}(99);
+                (a1, b, c) = this.s{accounts: meta, program_id: pid}(99);
                 require(a1 == 65535 && b && c.b4 == "naff", "b");
-                (a1, b, c) = this.m{accounts: meta}(1023413412);
+                (a1, b, c) = this.m{accounts: meta, program_id: pid}(1023413412);
                 require(a1 == 414243 && b && c.b4 == "niff", "c");
-                c.b4 = this.e{accounts: meta}();
+                c.b4 = this.e{accounts: meta, program_id: pid}();
                 require(a1 == 414243 && b && c.b4 == "cons", "E");
             }
         }"#,
@@ -283,10 +283,13 @@ fn struct_accessor() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
+    let program_id = vm.stack[0].id;
     vm.function("f")
+        .arguments(&[BorshToken::Address(program_id)])
         .accounts(vec![
             ("dataAccount", data_account),
             ("systemProgram", [0; 32]),
+            ("C_programId", program_id),
         ])
         .call();
 }

+ 11 - 12
tests/solana_tests/call.rs

@@ -4,7 +4,7 @@ use crate::{
     build_solidity, create_program_address, AccountMeta, AccountState, BorshToken, Instruction,
     Pubkey, VirtualMachine,
 };
-use base58::{FromBase58, ToBase58};
+use base58::FromBase58;
 use num_bigint::BigInt;
 use num_traits::One;
 
@@ -17,8 +17,8 @@ fn simple_external_call() {
                 print("bar0 says: " + v);
             }
 
-            function test_other(bar1 x) public {
-                x.test_bar("cross contract call");
+            function test_other(address x) public {
+                bar1.test_bar{program_id: x}("cross contract call");
             }
         }
 
@@ -86,8 +86,8 @@ fn external_call_with_returns() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other(bar1 x) public returns (int64) {
-                return x.test_bar(7) + 5;
+            function test_other(address x) public returns (int64) {
+                return bar1.test_bar{program_id: x}(7) + 5;
             }
         }
 
@@ -166,7 +166,7 @@ fn external_raw_call_with_returns() {
         contract bar0 {
             bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
 
-            function test_other(bar1 x) public returns (int64) {
+            function test_other(address x) public returns (int64) {
                 bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
                 bytes signature = abi.encodeWithSignature("global:test_bar", int64(7));
                 require(select == signature, "must be the same");
@@ -293,14 +293,14 @@ fn external_call_with_string_returns() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other(bar1 x) public returns (string) {
-                string y = x.test_bar(7);
+            function test_other(address x) public returns (string) {
+                string y = bar1.test_bar{program_id: x}(7);
                 print(y);
                 return y;
             }
 
-            function test_this(bar1 x) public {
-                address a = x.who_am_i();
+            function test_this(address x) public {
+                address a = bar1.who_am_i{program_id: x}();
                 assert(a == address(x));
             }
         }
@@ -392,7 +392,7 @@ fn encode_call() {
         contract bar0 {
             bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
 
-            function test_other(bar1 x) public returns (int64) {
+            function test_other(address x) public returns (int64) {
                 bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
                 bytes signature = abi.encodeCall(bar1.test_bar, 7);
                 require(select == signature, "must be the same");
@@ -439,7 +439,6 @@ fn encode_call() {
         .accounts(vec![("dataAccount", bar0_account)])
         .call();
 
-    std::println!("bar_acc: {}", bar1_account.to_base58());
     let res = vm
         .function("test_other")
         .arguments(&[BorshToken::Address(bar1_program_id)])

+ 30 - 52
tests/solana_tests/create_contract.rs

@@ -11,14 +11,12 @@ fn simple_create_contract_no_seed() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other() external returns (bar1) {
-                bar1 x = new bar1("yo from bar0");
-
-                return x;
+            function test_other() external {
+                bar1.new("yo from bar0");
             }
 
-            function call_bar1_at_address(bar1 a, string x) public {
-                a.say_hello(x);
+            function call_bar1_at_address(string x) public {
+                bar1.say_hello(x);
             }
         }
 
@@ -63,8 +61,7 @@ fn simple_create_contract_no_seed() {
         },
     );
 
-    let bar1 = vm
-        .function("test_other")
+    vm.function("test_other")
         .accounts(vec![
             ("bar1_dataAccount", acc),
             ("bar1_programId", program_id),
@@ -76,8 +73,7 @@ fn simple_create_contract_no_seed() {
             is_writable: true,
             is_signer: true,
         }])
-        .call()
-        .unwrap();
+        .call();
 
     assert_eq!(vm.logs, "bar1 says: yo from bar0");
 
@@ -86,7 +82,7 @@ fn simple_create_contract_no_seed() {
     vm.logs.truncate(0);
 
     vm.function("call_bar1_at_address")
-        .arguments(&[bar1, BorshToken::String(String::from("xywoleh"))])
+        .arguments(&[BorshToken::String(String::from("xywoleh"))])
         .accounts(vec![
             ("bar1_programId", program_id),
             ("systemProgram", [0; 32]),
@@ -101,14 +97,12 @@ fn simple_create_contract() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other() external returns (bar1) {
-                bar1 x = new bar1("yo from bar0");
-
-                return x;
+            function test_other() external {
+                bar1.new("yo from bar0");
             }
 
-            function call_bar1_at_address(bar1 a, string x) public {
-                a.say_hello(x);
+            function call_bar1_at_address(string x) public {
+                bar1.say_hello(x);
             }
         }
 
@@ -142,25 +136,21 @@ fn simple_create_contract() {
 
     vm.account_data.insert(payer, AccountState::default());
 
-    let bar1 = vm
-        .function("test_other")
+    vm.function("test_other")
         .accounts(vec![
             ("bar1_dataAccount", seed.0),
             ("bar1_programId", program_id),
             ("pay", payer),
             ("systemProgram", [0; 32]),
         ])
-        .call()
-        .unwrap();
+        .call();
 
     assert_eq!(vm.logs, "bar1 says: yo from bar0");
 
     vm.logs.truncate(0);
 
-    println!("next test, {bar1:?}");
-
     vm.function("call_bar1_at_address")
-        .arguments(&[bar1, BorshToken::String(String::from("xywoleh"))])
+        .arguments(&[BorshToken::String(String::from("xywoleh"))])
         .accounts(vec![
             ("bar1_programId", program_id),
             ("systemProgram", [0; 32]),
@@ -280,14 +270,12 @@ fn missing_contract() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other() external returns (bar1) {
-                bar1 x = new bar1("yo from bar0");
-
-                return x;
+            function test_other() external {
+                bar1.new("yo from bar0");
             }
 
-            function call_bar1_at_address(bar1 a, string x) public {
-                a.say_hello(x);
+            function call_bar1_at_address(string x) public {
+                bar1.say_hello(x);
             }
         }
 
@@ -337,7 +325,7 @@ fn two_contracts() {
         import 'solana';
 
         contract bar0 {
-            function test_other(address a, address b, address payer) external returns (bar1) {
+            function test_other(address a, address b, address payer) external {
                 AccountMeta[2] bar1_metas = [
                     AccountMeta({pubkey: a, is_writable: true, is_signer: true}),
                     AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
@@ -346,10 +334,8 @@ fn two_contracts() {
                     AccountMeta({pubkey: b, is_writable: true, is_signer: true}),
                     AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
                 ];
-                bar1 x = new bar1{accounts: bar1_metas}("yo from bar0");
-                bar1 y = new bar1{accounts: bar2_metas}("hi from bar0");
-
-                return x;
+                bar1.new{accounts: bar1_metas}("yo from bar0");
+                bar1.new{accounts: bar2_metas}("hi from bar0");
             }
         }
 
@@ -382,14 +368,16 @@ fn two_contracts() {
     vm.account_data.insert(seed2.0, AccountState::default());
     vm.account_data.insert(payer, AccountState::default());
 
-    let _bar1 = vm
-        .function("test_other")
+    vm.function("test_other")
         .arguments(&[
             BorshToken::Address(seed1.0),
             BorshToken::Address(seed2.0),
             BorshToken::Address(payer),
         ])
-        .accounts(vec![("systemProgram", [0; 32])])
+        .accounts(vec![
+            ("systemProgram", [0; 32]),
+            ("bar1_programId", program_id),
+        ])
         .remaining_accounts(&[
             AccountMeta {
                 pubkey: Pubkey(seed1.0),
@@ -401,11 +389,6 @@ fn two_contracts() {
                 is_signer: true,
                 is_writable: true,
             },
-            AccountMeta {
-                pubkey: Pubkey(program_id),
-                is_writable: false,
-                is_signer: false,
-            },
             AccountMeta {
                 pubkey: Pubkey(payer),
                 is_signer: true,
@@ -629,12 +612,10 @@ fn create_child() {
     let mut vm = build_solidity(
         r#"
         contract creator {
-            Child public c;
-
             function create_child() external {
                 print("Going to create child");
-                c = new Child();
-                c.say_hello();
+                Child.new();
+                Child.say_hello();
             }
         }
 
@@ -675,7 +656,6 @@ fn create_child() {
         .accounts(vec![
             ("Child_dataAccount", seed.0),
             ("Child_programId", child_program_id),
-            ("dataAccount", data_account),
             ("payer", payer),
             ("systemProgram", [0; 32]),
         ])
@@ -699,7 +679,6 @@ fn create_child_with_meta() {
         import 'solana';
 
 contract creator {
-    Child public c;
     function create_child_with_meta(address child, address payer) public {
         print("Going to create child");
         AccountMeta[2] metas = [
@@ -708,8 +687,8 @@ contract creator {
             // Passing the system account here crashes the VM, even if I add it to vm.account_data
             // AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
         ];
-        c = new Child{accounts: metas}();
-        c.say_hello();
+        Child.new{accounts: metas}();
+        Child.say_hello();
     }
 }
 
@@ -750,7 +729,6 @@ contract Child {
         .arguments(&[BorshToken::Address(seed.0), BorshToken::Address(payer)])
         .accounts(vec![
             ("Child_programId", child_program_id),
-            ("dataAccount", data_account),
             ("systemProgram", [0; 32]),
         ])
         .remaining_accounts(&[

+ 5 - 7
tests/solana_tests/mappings.rs

@@ -292,20 +292,18 @@ fn string_mapping() {
 fn contract_mapping() {
     let mut vm = build_solidity(
         r#"
-        interface I {}
+          contract foo {
+            mapping (address => string) public map;
 
-        contract foo {
-            mapping (I => string) public map;
-
-            function set(I index, string s) public {
+            function set(address index, string s) public {
                 map[index] = s;
             }
 
-            function get(I index) public returns (string) {
+            function get(address index) public returns (string) {
                 return map[index];
             }
 
-            function rm(I index) public {
+            function rm(address index) public {
                 delete map[index];
             }
         }"#,

+ 15 - 18
tests/solana_tests/runtime_errors.rs

@@ -10,9 +10,6 @@ fn runtime_errors() {
 contract RuntimeErrors {
     bytes b = hex"0000_00fa";
     uint256[] arr;
-    child public c;
-    child public c2;
-
     constructor() {}
 
     function print_test(int8 num) public returns (int8) {
@@ -149,7 +146,7 @@ contract calle_contract {
         .must_fail();
     assert_eq!(
         vm.logs,
-        "runtime_error: math overflow in test.sol:22:20-29,\n"
+        "runtime_error: math overflow in test.sol:19:20-29,\n"
     );
     vm.logs.clear();
 
@@ -163,7 +160,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: sesa require condition failed in test.sol:28:27-33,\n"
+        "runtime_error: sesa require condition failed in test.sol:25:27-33,\n"
     );
 
     vm.logs.clear();
@@ -175,7 +172,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: storage array index out of bounds in test.sol:48:19-23,\n"
+        "runtime_error: storage array index out of bounds in test.sol:45:19-23,\n"
     );
 
     vm.logs.clear();
@@ -187,7 +184,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: storage index out of bounds in test.sol:41:11-12,\n"
+        "runtime_error: storage index out of bounds in test.sol:38:11-12,\n"
     );
     vm.logs.clear();
 
@@ -201,7 +198,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: read integer out of bounds in test.sol:74:18-30,\n"
+        "runtime_error: read integer out of bounds in test.sol:71:18-30,\n"
     );
     vm.logs.clear();
 
@@ -215,7 +212,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: truncated type overflows in test.sol:79:37-42,\n"
+        "runtime_error: truncated type overflows in test.sol:76:37-42,\n"
     );
     vm.logs.clear();
 
@@ -223,7 +220,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: reached invalid instruction in test.sol:90:13-22,\n"
+        "runtime_error: reached invalid instruction in test.sol:87:13-22,\n"
     );
 
     vm.logs.clear();
@@ -235,7 +232,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: pop from empty storage array in test.sol:54:9-12,\n"
+        "runtime_error: pop from empty storage array in test.sol:51:9-12,\n"
     );
 
     vm.logs.clear();
@@ -250,7 +247,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: data does not fit into buffer in test.sol:69:18-28,\n"
+        "runtime_error: data does not fit into buffer in test.sol:66:18-28,\n"
     );
 
     vm.logs.clear();
@@ -265,7 +262,7 @@ contract calle_contract {
     println!("{}", vm.logs);
     assert_eq!(
         vm.logs,
-        "runtime_error: assert failure in test.sol:34:16-24,\n"
+        "runtime_error: assert failure in test.sol:31:16-24,\n"
     );
     vm.logs.clear();
 
@@ -279,7 +276,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: array index out of bounds in test.sol:85:16-21,\n"
+        "runtime_error: array index out of bounds in test.sol:82:16-21,\n"
     );
 
     vm.logs.clear();
@@ -294,7 +291,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: integer too large to write in buffer in test.sol:63:18-31,\n"
+        "runtime_error: integer too large to write in buffer in test.sol:60:18-31,\n"
     );
 
     vm.logs.clear();
@@ -309,7 +306,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: bytes cast error in test.sol:98:23-40,\n"
+        "runtime_error: bytes cast error in test.sol:95:23-40,\n"
     );
 
     vm.logs.clear();
@@ -318,7 +315,7 @@ contract calle_contract {
 
     assert_eq!(
         vm.logs,
-        "runtime_error: unspecified revert encountered in test.sol:58:9-17,\n"
+        "runtime_error: unspecified revert encountered in test.sol:55:9-17,\n"
     );
 
     vm.logs.clear();
@@ -326,7 +323,7 @@ contract calle_contract {
     _res = vm.function("revert_with_message").must_fail();
     assert_eq!(
         vm.logs,
-        "runtime_error: I reverted! revert encountered in test.sol:103:9-30,\n"
+        "runtime_error: I reverted! revert encountered in test.sol:100:9-30,\n"
     );
     assert!(vm.return_data.is_none());
 }

+ 119 - 92
tests/solana_tests/using.rs

@@ -1,98 +1,8 @@
 // SPDX-License-Identifier: Apache-2.0
 
+use crate::borsh_encoding::BorshToken;
 use crate::build_solidity;
-
-#[test]
-fn using_for_contracts() {
-    let mut runtime = build_solidity(
-        r#"
-        interface I {
-            function f(int) external;
-        }
-
-        library L {
-            function F(I i, bool b, int n) public {
-                if (b) {
-                    print("Hello");
-                }
-            }
-        }
-
-        contract C {
-            using L for I;
-
-            function test() public {
-                I i = I(address(0));
-
-                i.F(true, 102);
-            }
-        }"#,
-    );
-
-    let data_account = runtime.initialize_data_account();
-    runtime
-        .function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-    runtime.function("test").call();
-
-    assert_eq!(runtime.logs, "Hello");
-
-    let mut runtime = build_solidity(
-        r#"
-        interface I {
-            function f1(int) external;
-            function X(int) external;
-        }
-
-        library L {
-            function f1_2(I i) external {
-                i.f1(2);
-            }
-
-            function X(I i) external {
-                print("X lib");
-            }
-        }
-
-        contract foo is I {
-            using L for I;
-
-            function test() public {
-                I i = I(address(this));
-
-                i.X();
-                i.X(2);
-                i.f1_2();
-            }
-
-            function f1(int x) public {
-                print("x:{}".format(x));
-            }
-
-            function X(int) public {
-                print("X contract");
-            }
-        }"#,
-    );
-
-    let data_account = runtime.initialize_data_account();
-    runtime
-        .function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let program_id = runtime.stack[0].id;
-    runtime
-        .function("test")
-        .accounts(vec![
-            ("I_programId", program_id),
-            ("systemProgram", [0; 32]),
-        ])
-        .call();
-
-    assert_eq!(runtime.logs, "X libX contractx:2");
-}
+use num_bigint::BigInt;
 
 #[test]
 fn user_defined_oper() {
@@ -228,3 +138,120 @@ fn user_defined_oper() {
     runtime.function("test_arith").call();
     runtime.function("test_bit").call();
 }
+
+#[test]
+fn using_for_struct() {
+    let mut vm = build_solidity(
+        r#"
+struct Pet {
+    string name;
+    uint8 age;
+}
+
+library Info {
+    function isCat(Pet memory myPet) public pure returns (bool) {
+        return myPet.name == "cat";
+    }
+
+    function setAge(Pet memory myPet, uint8 age) pure public {
+        myPet.age = age;
+    }
+}
+
+contract C {
+    using Info for Pet;
+
+    function testPet(string memory name, uint8 age) pure public returns (bool) {
+        Pet memory my_pet = Pet(name, age);
+        return my_pet.isCat();
+    }
+
+    function changeAge(Pet memory myPet) public pure returns (Pet memory) {
+        myPet.setAge(5);
+        return myPet;
+    }
+
+}
+        "#,
+    );
+
+    let data_account = vm.initialize_data_account();
+
+    vm.function("new")
+        .accounts(vec![("dataAccount", data_account)])
+        .call();
+
+    let res = vm
+        .function("testPet")
+        .arguments(&[
+            BorshToken::String("cat".to_string()),
+            BorshToken::Uint {
+                width: 8,
+                value: BigInt::from(2u8),
+            },
+        ])
+        .call()
+        .unwrap();
+
+    assert_eq!(res, BorshToken::Bool(true));
+
+    let res = vm
+        .function("changeAge")
+        .arguments(&[BorshToken::Tuple(vec![
+            BorshToken::String("cat".to_string()),
+            BorshToken::Uint {
+                width: 8,
+                value: BigInt::from(2u8),
+            },
+        ])])
+        .call()
+        .unwrap();
+
+    assert_eq!(
+        res,
+        BorshToken::Tuple(vec![
+            BorshToken::String("cat".to_string()),
+            BorshToken::Uint {
+                width: 8,
+                value: BigInt::from(5u8),
+            }
+        ])
+    );
+}
+
+#[test]
+fn using_overload() {
+    let mut vm = build_solidity(
+        r#"
+        library MyBytes {
+    function push(bytes memory b, uint8[] memory a) pure public returns (bool) {
+        return b[0] == bytes1(a[0]) && b[1] == bytes1(a[1]);
+    }
+}
+
+contract C {
+    using MyBytes for bytes;
+
+    function check() public pure returns (bool) {
+        bytes memory b;
+        b.push(1);
+        b.push(2);
+        uint8[] memory vec = new uint8[](2);
+        vec[0] = 1;
+        vec[1] = 2;
+        return b.push(vec);
+    }
+}
+
+        "#,
+    );
+
+    let data_account = vm.initialize_data_account();
+    vm.function("new")
+        .accounts(vec![("dataAccount", data_account)])
+        .call();
+
+    let res = vm.function("check").call().unwrap();
+
+    assert_eq!(res, BorshToken::Bool(true));
+}