Skip to content

Commit

Permalink
fix: resolve potential semantic incompatibility with super() usage an…
Browse files Browse the repository at this point in the history
…d differing kinds of attributes (methods vs data)

add bad super() usage test
  • Loading branch information
achidlow committed Jun 25, 2024
1 parent f6e2ee3 commit f1f2bdd
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 17 deletions.
41 changes: 24 additions & 17 deletions src/puya/awst_build/subroutine.py
Original file line number Diff line number Diff line change
Expand Up @@ -1260,26 +1260,33 @@ def visit_super_expr(self, super_expr: mypy.nodes.SuperExpr) -> ExpressionBuilde
self._location(super_expr.call),
)
for base in iterate_user_bases(self.contract_method_info.type_info):
if (base_method := base.get_method(super_expr.name)) is not None:
cref = qualified_class_name(base)
if not isinstance(base_method.type, mypy.types.CallableType):
# this shouldn't be hit unless there's typing.overload of weird
# decorators going on, both of which we don't allow
base_sym = base.get(super_expr.name)
if base_sym is None:
continue
match base_sym.node:
case None:
raise CodeError(
f"Unable to retrieve type of {cref.full_name}.{super_expr.name}",
super_loc,
f"Unable to resolve type of member {super_expr.name!r}", super_loc
)
return SubroutineInvokerExpressionBuilder(
context=self.context,
target=BaseClassSubroutineTarget(base_class=cref, name=super_expr.name),
location=super_loc,
func_type=base_method.type,
)
# matching types taken from mypy.nodes.TypeInfo.get_method
case (mypy.nodes.FuncBase() | mypy.nodes.Decorator()) as base_method:
cref = qualified_class_name(base)
if not isinstance(base_method.type, mypy.types.CallableType):
# this shouldn't be hit unless there's typing.overload or weird
# decorators going on, both of which we don't allow
raise CodeError(
f"Unable to retrieve type of {cref.full_name}.{super_expr.name}",
super_loc,
)
return SubroutineInvokerExpressionBuilder(
context=self.context,
target=BaseClassSubroutineTarget(base_class=cref, name=super_expr.name),
location=super_loc,
func_type=base_method.type,
)
case _:
raise CodeError("super() is only supported for method calls", super_loc)

if super_expr.name in self.contract_method_info.app_storage:
raise CodeError(
"super() is only supported for method calls, not member access", super_loc
)
raise CodeError(
f"Unable to locate method {super_expr.name}"
f" in bases of {self.contract_method_info.cref.full_name}",
Expand Down
12 changes: 12 additions & 0 deletions tests/test_expected_output/expected_errors.test
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,15 @@ def unsupported_biguint_operator() -> None:
def unsupported_uint64_operator() -> None:
assert UInt64() @ 2 # type: ignore[operator,misc] ## E: Unsupported UInt64 math operator '@'


## case: super_with_data_member
from algopy import *

class Base(ARC4Contract):
def __init__(self) -> None:
self.foo = UInt64(1)


class Derived(Base):
def __init__(self) -> None:
self.foo2 = 2 * super().foo ## E: super() is only supported for method calls

0 comments on commit f1f2bdd

Please sign in to comment.