Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions changelog.d/20260205_100910_bobby.lat_arg_check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!--
A new scriv changelog fragment.

Uncomment the section that is right (remove the HTML comment wrapper).
For top level release notes, leave all the headers commented out.
-->

<!--
### Removed

- A bullet item for the Removed category.

-->
<!--
### Added

- A bullet item for the Added category.

-->

### Changed

- Improved error message for unsupported ARC-4 method types to provide more accurate source locations.

<!--
### Deprecated

- A bullet item for the Deprecated category.

-->
<!--
### Fixed

- A bullet item for the Fixed category.

-->
<!--
### Security

- A bullet item for the Security category.

-->
5 changes: 4 additions & 1 deletion src/puya/ir/arc4_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,10 @@ def wtype_to_arc4(
return wtype.name
maybe_arc4_wtype = maybe_wtype_to_arc4_wtype(wtype)
if maybe_arc4_wtype is None:
raise CodeError("unsupported type for an ARC-4 method", loc)
error_msg = "unsupported type for an ARC-4 method"
if is_return:
error_msg = "unsupported return type for an ARC-4 method"
raise CodeError(error_msg, loc)
return get_arc4_name(maybe_arc4_wtype, use_alias=True)


Expand Down
30 changes: 0 additions & 30 deletions src/puyapy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
ContractMethod,
StateTotals,
)
from puya.errors import CodeError
from puya.parse import SourceLocation
from puya.program_refs import ContractReference
from puyapy.awst_build import pytypes
Expand Down Expand Up @@ -51,25 +50,6 @@ class ARC4ABIMethodData:
config: ARC4ABIMethodConfig
is_bare: bool = attrs.field(default=False, init=False)
_signature: dict[str, pytypes.PyType]
_arc4_signature: Mapping[str, pytypes.PyType] = attrs.field(init=False)

@_arc4_signature.default
def _arc4_signature_default(self) -> Mapping[str, pytypes.PyType]:
from puyapy.awst_build.arc4_utils import pytype_to_arc4_pytype # TODO: resolve circularity

def on_error(bad_type: pytypes.PyType, loc: SourceLocation | None) -> typing.Never:
raise CodeError(f"invalid type for an ARC-4 method: {bad_type}", loc)

pass_resources_by_value = self.config.resource_encoding == "value"
return {
k: pytype_to_arc4_pytype(
v,
on_error=on_error,
encode_resource_types=pass_resources_by_value or k == "output",
source_location=self.source_location,
)
for k, v in self._signature.items()
}

@property
def signature(self) -> Mapping[str, pytypes.PyType]:
Expand All @@ -79,22 +59,12 @@ def signature(self) -> Mapping[str, pytypes.PyType]:
def return_type(self) -> pytypes.PyType:
return self._signature["output"]

@cached_property
def arc4_return_type(self) -> pytypes.PyType:
return self._arc4_signature["output"]

@cached_property
def argument_types(self) -> Sequence[pytypes.PyType]:
names, types = zip(*self._signature.items(), strict=True)
assert names[-1] == "output"
return tuple(types[:-1])

@cached_property
def arc4_argument_types(self) -> Sequence[pytypes.PyType]:
names, types = zip(*self._arc4_signature.items(), strict=True)
assert names[-1] == "output"
return tuple(types[:-1])


ARC4MethodData: typing.TypeAlias = ARC4BareMethodData | ARC4ABIMethodData

Expand Down
22 changes: 14 additions & 8 deletions tests/test_expected_output/arc4.test
Original file line number Diff line number Diff line change
Expand Up @@ -529,14 +529,7 @@ class MyContract(ARC4Contract): ## E: Non-abstract ARC-4 contract has no methods
def b(self) -> None:
pass

@arc4.abimethod ## E: invalid type for an ARC-4 method: algopy.BoxRef
def c(self, box: BoxRef) -> None:
pass

@arc4.abimethod ## E: invalid type for an ARC-4 method: algopy.BoxRef
def d(self, arg: Bytes) -> BoxRef:
return BoxRef(key=arg)


@arc4.baremethod # type: ignore[arg-type] ## E: bare methods should have no arguments or return values
def e(self, x: UInt64) -> None:
pass
Expand All @@ -546,6 +539,19 @@ class MyContract(ARC4Contract): ## E: Non-abstract ARC-4 contract has no methods
return UInt64(0)


## case: bad_signatures_from_ir_layer
from algopy import *

class MyContract(ARC4Contract):

@arc4.abimethod
def c(self, box: BoxRef) -> None: ## E: unsupported type for an ARC-4 method
pass

@arc4.abimethod ## E: unsupported return type for an ARC-4 method
def d(self, arg: Bytes) -> BoxRef:
return BoxRef(key=arg)

## case: test_string
from algopy import arc4, subroutine

Expand Down
42 changes: 27 additions & 15 deletions tests/test_expected_output/arrays.test
Original file line number Diff line number Diff line change
Expand Up @@ -109,31 +109,43 @@ class ArrayContract(arc4.ARC4Contract):
def imm_itxn2_arg(self, arr: ImmutableArray[itxn.PaymentInnerTransaction]) -> None: ## E: arrays can only contain persistable elements
pass

@arc4.abimethod ## E: invalid type for an ARC-4 method: algopy.ReferenceArray[algopy.gtxn.Transaction]
def arr_txn_arg(self, arr: ReferenceArray[gtxn.Transaction]) -> None:
@arc4.abimethod()
def test_reference_array_extend_with_arc4_bool(self) -> None:
arr = ReferenceArray[arc4.Bool]()
dyn_arr = arc4.DynamicArray[arc4.Bool]()
arr.extend(dyn_arr) ## E: extending a reference array with an ARC-4 encoded bool type is not supported

## case: test_array_element_error_from_ir_layer
import typing
from algopy import *


class ArrayContract(arc4.ARC4Contract):

@arc4.abimethod
def arr_txn_arg(self, arr: ReferenceArray[gtxn.Transaction]) -> None: ## E: unsupported type for an ARC-4 method
pass

@arc4.abimethod ## E: invalid type for an ARC-4 method: algopy.ReferenceArray[algopy.itxn.Payment]
def arr_itxn_arg(self, arr: ReferenceArray[itxn.Payment]) -> None:
@arc4.abimethod
def arr_itxn_arg(self, arr: ReferenceArray[itxn.Payment]) -> None: ## E: unsupported type for an ARC-4 method
pass

@arc4.abimethod ## E: invalid type for an ARC-4 method: algopy.ReferenceArray[algopy.itxn.PaymentInnerTransaction]
def arr_itxn2_arg(self, arr: ReferenceArray[itxn.PaymentInnerTransaction]) -> None:
@arc4.abimethod
def arr_itxn2_arg(self, arr: ReferenceArray[itxn.PaymentInnerTransaction]) -> None: ## E: unsupported type for an ARC-4 method
pass

@arc4.abimethod ## E: invalid type for an ARC-4 method: algopy.ReferenceArray[algopy.Asset]
def arr_asset_arg(self, arr: ReferenceArray[Asset]) -> None:
@arc4.abimethod
def arr_asset_arg(self, arr: ReferenceArray[Asset]) -> None: ## E: unsupported type for an ARC-4 method
pass

@arc4.abimethod ## E: invalid type for an ARC-4 method: algopy.ReferenceArray[algopy.Account]
def arr_account_arg(self, arr: ReferenceArray[Account]) -> None:
@arc4.abimethod
def arr_account_arg(self, arr: ReferenceArray[Account]) -> None: ## E: unsupported type for an ARC-4 method
pass

@arc4.abimethod()
def test_reference_array_extend_with_arc4_bool(self) -> None:
arr = ReferenceArray[arc4.Bool]()
dyn_arr = arc4.DynamicArray[arc4.Bool]()
arr.extend(dyn_arr) ## E: extending a reference array with an ARC-4 encoded bool type is not supported
@arc4.abimethod ## E: unsupported return type for an ARC-4 method
def arr_account_return(self) -> ReferenceArray[Account]:
op.err("Not implemented")


## case: test_imm_array_txn
from algopy import *
Expand Down
Loading