Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat[lang]: singleton modules with ownership hierarchy #3729

Merged
merged 164 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
164 commits
Select commit Hold shift + click to select a range
4cf4cd7
allow walrus operator
charles-cooper Jan 13, 2024
dad2072
enforce single visit of modules
charles-cooper Jan 13, 2024
c7819e6
add UsesDecl and InitializesDecl nodes
charles-cooper Jan 13, 2024
f524fd9
add special visibility for the __init__ function
charles-cooper Dec 27, 2023
7431780
add compilation for init functions
charles-cooper Jan 13, 2024
9d6a3dc
fix an assert
charles-cooper Jan 13, 2024
672441d
Merge branch 'master' into feat/singleton_modules
charles-cooper Jan 14, 2024
46140f4
fix bad variable name in vyper.ast.nodes
charles-cooper Jan 14, 2024
d6d7de7
remove dead variable: Context.in_assertion
charles-cooper Jan 14, 2024
b67d361
get __init__() working both as entry point and as internal function
charles-cooper Jan 14, 2024
1089ee0
add sanity checks
charles-cooper Jan 14, 2024
6bb650b
update storage allocator - recurse into `initialized` modules
charles-cooper Jan 15, 2024
c3d5765
use tagged expr_info instead of context.globals
charles-cooper Jan 15, 2024
4a1f1d8
fix get_expr_info for nested moduleinfo
charles-cooper Jan 15, 2024
f89b118
fix missing expr_info
charles-cooper Jan 15, 2024
c1ca06f
add simple storage variable
charles-cooper Jan 19, 2024
6adbb5a
add more complicated test
charles-cooper Jan 19, 2024
99f9b8a
Merge branch 'master' into feat/singleton_modules
charles-cooper Feb 2, 2024
468b35e
implement borrowship checker
charles-cooper Feb 2, 2024
de98cbc
add uses check for module writes
charles-cooper Feb 2, 2024
d82eb3e
add global initialization constraint
charles-cooper Feb 3, 2024
82e1da0
fix mypy
charles-cooper Feb 3, 2024
9d94161
fix duplicate initialization check, rename FunctionNodeVisitor to Fun…
charles-cooper Feb 3, 2024
21b753a
fix some mypy
charles-cooper Feb 3, 2024
629f2e6
add missing case
charles-cooper Feb 3, 2024
72bf1ba
simplify mutability check
charles-cooper Feb 3, 2024
63aaa8d
fix a bad exception type
charles-cooper Feb 3, 2024
07aa416
rename CONSTRUCTOR visibility to DEPLOY, and make it user-facing
charles-cooper Feb 3, 2024
cf942ab
fix a lint
charles-cooper Feb 3, 2024
21fe23d
add a comment
charles-cooper Feb 3, 2024
27dfd6b
add init function check
charles-cooper Feb 3, 2024
b10ce08
move initializer check to module analysis
charles-cooper Feb 3, 2024
3e16f2d
add uses check
charles-cooper Feb 3, 2024
15057aa
refactor: move global initializer constraint checker to global.py
charles-cooper Feb 3, 2024
6a227d9
add a check that deploy functions can only be called from deploy func…
charles-cooper Feb 3, 2024
5bc40b8
fix lint
charles-cooper Feb 3, 2024
7b8aebb
pretty up an error message
charles-cooper Feb 3, 2024
3c2fbf3
some fixes
charles-cooper Feb 4, 2024
34ca482
fix scope_name for ExprVisitor
charles-cooper Feb 4, 2024
1e91a63
fix stray debug
charles-cooper Feb 4, 2024
2a52622
add recursive allocator for immutables
charles-cooper Feb 4, 2024
61310c0
add a comment
charles-cooper Feb 4, 2024
34244c4
refactor: move validate_modification to analysis/utils.py
charles-cooper Feb 4, 2024
8472c29
move validate_numeric_op from validate_modification directly into vis…
charles-cooper Feb 4, 2024
2fd7848
refactor validate_modification: remove `node` parameter
charles-cooper Feb 4, 2024
446c70a
fix tag_exceptions, again
charles-cooper Feb 4, 2024
e99c7af
fix circular import
charles-cooper Feb 4, 2024
a1c177d
refactor: rename _StringEnum to StringEnum
charles-cooper Feb 4, 2024
70a9b56
make DataLocation a StringEnum
charles-cooper Feb 4, 2024
255669d
add a comment
charles-cooper Feb 4, 2024
7f296e7
dynamic programming
charles-cooper Feb 4, 2024
e0174fd
fix module use checker
charles-cooper Feb 4, 2024
462a7b4
small fix
charles-cooper Feb 4, 2024
24abdd8
improve modifiability check
charles-cooper Feb 4, 2024
4981710
fix: bad assertion in expr.py
charles-cooper Feb 4, 2024
032b8b5
improve an error
charles-cooper Feb 4, 2024
aff0fb4
fix for unfolded constant variables
charles-cooper Feb 5, 2024
27770ef
spruce up some error messages
charles-cooper Feb 5, 2024
ccb5a68
Merge branch 'master' into feat/singleton_modules
charles-cooper Feb 5, 2024
a490eee
Merge branch 'master' into feat/singleton_modules
charles-cooper Feb 5, 2024
8902991
fix mypy
charles-cooper Feb 5, 2024
e51756c
Merge branch 'master' into feat/singleton_modules
charles-cooper Feb 5, 2024
70e0226
get rid of bad Index nodes
charles-cooper Feb 5, 2024
3ebd2d3
update a bunch of __init__ functions visibility to `@deploy` from `@e…
charles-cooper Feb 5, 2024
0945835
fix a bug
charles-cooper Feb 5, 2024
00aa0bf
fix another bug
charles-cooper Feb 5, 2024
142ae33
change a bunch of __init__ function visibilities - `@external` to `@d…
charles-cooper Feb 5, 2024
0d7052e
fix abi -- emit constructor in abi
charles-cooper Feb 5, 2024
58937a8
use TYPE_T in functions instead of "TYPE_DEFINITION"
charles-cooper Feb 5, 2024
3477373
move hashmap check into handle_modification
charles-cooper Feb 5, 2024
c74e74d
refactor out check_call_modifiability
charles-cooper Feb 5, 2024
dd94476
Revert "refactor out check_call_modifiability"
charles-cooper Feb 5, 2024
fd0fb67
refactor out check_call_mutability (take 2)
charles-cooper Feb 5, 2024
6a5434e
fix a test
charles-cooper Feb 5, 2024
3587ef1
stray @external -> @deploy
charles-cooper Feb 5, 2024
7b06f5d
fix: don't require call to __init__ when module has no __init__ function
charles-cooper Feb 5, 2024
253b924
fix: panic when mutating complex immutables
charles-cooper Feb 5, 2024
58358c1
fix a test
charles-cooper Feb 5, 2024
bbd0c13
update a test
charles-cooper Feb 5, 2024
61aac63
update an error message
charles-cooper Feb 5, 2024
168c939
update dead code eliminator tests
charles-cooper Feb 5, 2024
8952c6d
fix lint
charles-cooper Feb 5, 2024
89eff74
fix mypy
charles-cooper Feb 5, 2024
1d331ce
add NamedExpr to vyper/ast/nodes.pyi
charles-cooper Feb 6, 2024
84c5188
fix: bad error message in constant folder
charles-cooper Feb 6, 2024
125ae11
add hint functionality to VyperException
charles-cooper Feb 6, 2024
ffb9deb
improve an error message
charles-cooper Feb 6, 2024
8ed92d4
format an error message
charles-cooper Feb 6, 2024
0d3b032
refactor some hints
charles-cooper Feb 6, 2024
10f3aeb
refactor out find_module routine
charles-cooper Feb 6, 2024
1133674
fix a type signature
charles-cooper Feb 6, 2024
316456a
fix global initializer constraint and improve an error message
charles-cooper Feb 6, 2024
76db10c
fix a function name
charles-cooper Feb 6, 2024
02ca269
make ModuleOwnership a StringEnum, fix an error message
charles-cooper Feb 6, 2024
8950848
test initializer init function
charles-cooper Feb 6, 2024
b7ba6f1
fix ordering of Modifiability things
charles-cooper Feb 6, 2024
ad09356
fix modifiability propagation
charles-cooper Feb 6, 2024
3f74ed8
refactor: Attribute.get_attribute_root()
charles-cooper Feb 6, 2024
ba80865
improve some error messages
charles-cooper Feb 6, 2024
cf3c3e3
fix an error message
charles-cooper Feb 6, 2024
c8deffd
add tests for initializer constraints
charles-cooper Feb 6, 2024
8bb4399
fix mypy; refactor get_attribute_root
charles-cooper Feb 6, 2024
afcef72
can't call `@deploy` function from `@internal` function
charles-cooper Feb 6, 2024
74f6ffb
fix: unused modules cannot be used as default arguments
charles-cooper Feb 6, 2024
18bb4d0
fix lint
charles-cooper Feb 6, 2024
a7ca410
add a couple more tests
charles-cooper Feb 6, 2024
aa5b802
fix variable writes analysis
charles-cooper Feb 6, 2024
afb5dd0
fix some tests
charles-cooper Feb 6, 2024
b697f24
refactor module use analysis
charles-cooper Feb 6, 2024
865661a
ban module state reads without `uses`
charles-cooper Feb 6, 2024
c8f194a
fix mypy
charles-cooper Feb 6, 2024
81c246c
fix a stupid bug -- refactor get_expr_info to handle callables
charles-cooper Feb 6, 2024
389d3c0
fix handle_modification for tuples
charles-cooper Feb 6, 2024
dad54f0
fix handle_modification for subscripts
charles-cooper Feb 7, 2024
ee271f1
add test for `uses` hashmap
charles-cooper Feb 7, 2024
ca12961
fix bad modifiability check
charles-cooper Feb 7, 2024
de29d46
fix lint
charles-cooper Feb 7, 2024
fbe19b0
fix another bug
charles-cooper Feb 7, 2024
708867b
add a test for tuples
charles-cooper Feb 7, 2024
8d56d81
simplify module use analysis and fix variable use analysis
charles-cooper Feb 7, 2024
e2e15cd
add some mutability tests
charles-cooper Feb 7, 2024
cbef6bc
add more mutability tests
charles-cooper Feb 7, 2024
8d75e4d
fix lint
charles-cooper Feb 7, 2024
a344739
add a test for empty builtin
charles-cooper Feb 7, 2024
84841e9
test more complex types
charles-cooper Feb 7, 2024
86892cc
refactor variable access analysis
charles-cooper Feb 7, 2024
f510ddd
add some more tests for nested attributes
charles-cooper Feb 7, 2024
6f17271
Merge branch 'master' into feat/singleton_modules
charles-cooper Feb 7, 2024
320c549
fix `uses` analysis for chain of modules
charles-cooper Feb 7, 2024
0288807
remove an xfail
charles-cooper Feb 7, 2024
8fdb0c9
add a docstring
charles-cooper Feb 7, 2024
5b63573
add uses test for nonpayable function which doesn't touch any module …
charles-cooper Feb 7, 2024
5b703e0
fix codegen for imported immutables and add a test
charles-cooper Feb 7, 2024
c47f1d3
add more reads/uses tests
charles-cooper Feb 7, 2024
bdbc48d
fix lark grammar -- add walrus operator
charles-cooper Feb 7, 2024
ca58f43
add codegen test for `uses`
charles-cooper Feb 7, 2024
353699c
test a rename
charles-cooper Feb 7, 2024
d9293e7
add some more tests
charles-cooper Feb 7, 2024
7bde8ec
add another test for uses/initializes codegen
charles-cooper Feb 7, 2024
ca77ccc
fix a comment
charles-cooper Feb 7, 2024
7ca5876
fixup storage layout export and add tests for storage layout
charles-cooper Feb 7, 2024
618e06a
add separate transient storage allocator
charles-cooper Feb 8, 2024
dc8bbbe
add storage layout tests for nested uses/initializes
charles-cooper Feb 8, 2024
ba6fb9a
fix mypy
charles-cooper Feb 8, 2024
b706c85
add tests for init function visibility
charles-cooper Feb 8, 2024
f95f440
fix lint
charles-cooper Feb 8, 2024
ac8d26a
add a couple more deploy visibility tests
charles-cooper Feb 8, 2024
524a7e8
improve a couple error messages
charles-cooper Feb 8, 2024
c33facc
test cannot call @deploy from @external function
charles-cooper Feb 8, 2024
f34923b
add pragma version to all examples
charles-cooper Feb 8, 2024
2dc82a1
fix lint
charles-cooper Feb 8, 2024
4fca078
fix a bug
charles-cooper Feb 8, 2024
0957c9b
Merge branch 'master' into feat/singleton_modules
charles-cooper Feb 8, 2024
82735c4
fix typo -- missing space
charles-cooper Feb 8, 2024
e393bd7
remove dead function
charles-cooper Feb 9, 2024
5b7b3b8
add tests for multiple uses
charles-cooper Feb 9, 2024
bdd3dea
fix: swallowed errors result in bad state
charles-cooper Feb 9, 2024
161d3ac
fix lint
charles-cooper Feb 9, 2024
5c30bf4
fix: interfaces access from .vyi files
charles-cooper Feb 9, 2024
e16ab99
add a test for implements via imported vyi file
charles-cooper Feb 9, 2024
96699f9
fix lint
charles-cooper Feb 9, 2024
f5186b8
fix importing types from nested modules
charles-cooper Feb 9, 2024
87afe01
add missing StringEnum methods
charles-cooper Feb 9, 2024
94d0215
hygiene: replace a usage of StringEnum.value
charles-cooper Feb 10, 2024
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
93 changes: 87 additions & 6 deletions vyper/ast/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,20 @@
if ast_struct["value"] is not None:
_raise_syntax_exc("`implements` cannot have a value assigned", ast_struct)
ast_struct["ast_type"] = "ImplementsDecl"

# Replace "uses:" `AnnAssign` nodes with `UsesDecl`
elif getattr(ast_struct["target"], "id", None) == "uses":
if ast_struct["value"] is not None:
_raise_syntax_exc("`uses` cannot have a value assigned", ast_struct)
ast_struct["ast_type"] = "UsesDecl"

# Replace "initializes:" `AnnAssign` nodes with `InitializesDecl`
elif getattr(ast_struct["target"], "id", None) == "initializes":
if ast_struct["value"] is not None:
_raise_syntax_exc("`initializes` cannot have a value assigned", ast_struct)
ast_struct["ast_type"] = "InitializesDecl"

# Replace state and local variable declarations `AnnAssign` with `VariableDecl`
# Parent node is required for context to determine whether replacement should happen.
else:
ast_struct["ast_type"] = "VariableDecl"

Expand Down Expand Up @@ -718,6 +730,19 @@
__slots__ = ("value",)


class NamedExpr(Stmt):
__slots__ = ("target", "value")

def validate(self):
# module[dep1 := dep2]

# XXX: better error messages
if not isinstance(self.target, Name):
raise StructureException("not a Name")

if not isinstance(self.value, Name):
raise StructureException("not a Name")

class Log(Stmt):
__slots__ = ("value",)

Expand Down Expand Up @@ -1357,24 +1382,80 @@
"""
An `implements` declaration.

Excludes `simple` and `value` attributes from Python `AnnAssign` node.

Attributes
----------
target : Name
Name node for the `implements` keyword
annotation : Name
Name node for the interface to be implemented
"""

__slots__ = ("target", "annotation")
__slots__ = ("annotation",)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

if not isinstance(self.annotation, (Name, Attribute)):
raise StructureException("invalid implements", self.annotation)

class UsesDecl(Stmt):
"""
A `uses` declaration.

Attributes
----------
annotation : Name | Attribute | Tuple
The module(s) which this uses
"""
__slots__ = ("annotation",)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

if isinstance(self.annotation, Tuple):
items = self.annotation.elements
else:
items = (self.annotation,)

for item in items:
if not isinstance(item, (Name, Attribute)):
raise StructureException("invalid uses", item)

class InitializesDecl(Stmt):
"""
An `initializes` declaration.

Attributes
----------
annotation : Name | Attribute | Subscript
An imported module which this module initializes
"""
__slots__ = ("annotation",)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

module_ref = self.annotation
if isinstance(module_ref, Subscript):
module_ref = module_ref.value

index = self.annotation.slice.value

if isinstance(index, Tuple):
dependencies = dependencies.elements
Fixed Show fixed Hide fixed
else:
dependencies = (index,)

for item in dependencies:
if not isinstance(item, NamedExpr):
print(type(item))
raise StructureException("invalid dependency (hint: should be [dependency := dependency]", item)
if not isinstance(item.target, (Name, Attribute)):
raise StructureException("invalid module", item.target)
if not isinstance(item.value, (Name, Attribute)):
raise StructureException("invalid module", item.target)

if not isinstance(module_ref, (Name, Attribute)):
raise StructureException("invalid module", module_ref)


class If(Stmt):
__slots__ = ("test", "body", "orelse")
Expand Down
4 changes: 3 additions & 1 deletion vyper/ast/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,9 @@ def visit_Subscript(self, node):

Starting from python 3.9, the `Index` node type has been deprecated,
and made impossible to instantiate via regular means. Here we do awful
hacky black magic to create an `Index` node. We need our own parser.
hacky black magic to create an `Index` node.

TODO: remove Index ast nodes since we require python>=3.10 now - CMC 2024-01-13
"""
self.generic_visit(node)

Expand Down
2 changes: 1 addition & 1 deletion vyper/codegen/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ def parse_Call(self):
return pop_dyn_array(darray, return_popped_item=True)

if isinstance(func_type, ContractFunctionT):
if func_type.is_internal:
if func_type.is_internal or func_type.is_constructor:
return self_call.ir_for_self_call(self.expr, self.context)
else:
return external_call.ir_for_external_call(self.expr, self.context)
Expand Down
12 changes: 9 additions & 3 deletions vyper/codegen/function_definitions/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ def external_function_base_entry_label(self) -> str:
return self.ir_identifier + "_common"

def internal_function_label(self, is_ctor_context: bool = False) -> str:
assert self.func_t.is_internal, "uh oh, should be internal"
f = self.func_t
assert f.is_internal or f.is_constructor, "uh oh, should be internal"

if f.is_constructor:
# sanity check - imported init functions only callable from main init
assert is_ctor_context

suffix = "_deploy" if is_ctor_context else "_runtime"
return self.ir_identifier + suffix

Expand Down Expand Up @@ -140,7 +146,7 @@ def generate_ir_for_function(
is_ctor_context=is_ctor_context,
)

if func_t.is_internal:
if func_t.is_internal or func_t.is_constructor:
ret: FuncIR = InternalFuncIR(generate_ir_for_internal_function(code, func_t, context))
func_t._ir_info.gas_estimate = ret.func_ir.gas # type: ignore
else:
Expand All @@ -163,7 +169,7 @@ def generate_ir_for_function(
else:
assert frame_info == func_t._ir_info.frame_info

if not func_t.is_internal:
if func_t.is_external:
# adjust gas estimate to include cost of mem expansion
# frame_size of external function includes all private functions called
# (note: internal functions do not need to adjust gas estimate since
Expand Down
18 changes: 8 additions & 10 deletions vyper/codegen/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,12 +424,13 @@ def _selector_section_linear(external_functions, module_ctx):

# take a ModuleT, and generate the runtime and deploy IR
def generate_ir_for_module(module_ctx: ModuleT) -> tuple[IRnode, IRnode]:
# XXX: rename `module_ctx` to `compilation_target`
# order functions so that each function comes after all of its callees
function_defs = _topsort(module_ctx.function_defs)
reachable = _globally_reachable_functions(module_ctx.function_defs)

runtime_functions = [f for f in function_defs if not _is_constructor(f)]
init_function = next((f for f in function_defs if _is_constructor(f)), None)
init_function = next((f for f in module_ctx.function_defs if _is_constructor(f)), None)

internal_functions = [f for f in runtime_functions if _is_internal(f)]

Expand Down Expand Up @@ -475,18 +476,15 @@ def generate_ir_for_module(module_ctx: ModuleT) -> tuple[IRnode, IRnode]:

deploy_code: List[Any] = ["seq"]
immutables_len = module_ctx.immutable_section_bytes
if init_function:
if init_function is not None:
# cleanly rerun codegen for internal functions with `is_ctor_ctx=True`
init_func_t = init_function._metadata["func_type"]
ctor_internal_func_irs = []
internal_functions = [f for f in runtime_functions if _is_internal(f)]
for f in internal_functions:
func_t = f._metadata["func_type"]
if func_t not in init_func_t.reachable_internal_functions:
# unreachable code, delete it
continue

func_ir = _ir_for_internal_function(f, module_ctx, is_ctor_context=True)

reachable_from_ctor = init_func_t.reachable_internal_functions
for func_t in reachable_from_ctor:
fn_ast = func_t.ast_def
func_ir = _ir_for_internal_function(fn_ast, module_ctx, is_ctor_context=True)
ctor_internal_func_irs.append(func_ir)

# generate init_func_ir after callees to ensure they have analyzed
Expand Down
2 changes: 1 addition & 1 deletion vyper/codegen/stmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def parse_Call(self):
return pop_dyn_array(darray, return_popped_item=False)

if isinstance(func_type, ContractFunctionT):
if func_type.is_internal:
if func_type.is_internal or func_type.is_constructor:
return self_call.ir_for_self_call(self.stmt, self.context)
else:
return external_call.ir_for_external_call(self.stmt, self.context)
Expand Down
17 changes: 12 additions & 5 deletions vyper/semantics/analysis/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ def values(cls) -> List[str]:
# Comparison operations
def __eq__(self, other: object) -> bool:
if not isinstance(other, self.__class__):
raise CompilerPanic("Can only compare like types.")
raise CompilerPanic("bad comparison")
return self is other

# Python normally does __ne__(other) ==> not self.__eq__(other)

def __lt__(self, other: object) -> bool:
if not isinstance(other, self.__class__):
raise CompilerPanic("Can only compare like types.")
raise CompilerPanic("bad comparison")
options = self.__class__.options()
return options.index(self) < options.index(other) # type: ignore

Expand All @@ -68,9 +68,16 @@ def __ge__(self, other: object) -> bool:


class FunctionVisibility(_StringEnum):
# TODO: these can just be enum.auto() right?
EXTERNAL = _StringEnum.auto()
INTERNAL = _StringEnum.auto()
EXTERNAL = enum.auto()
INTERNAL = enum.auto()
CONSTRUCTOR = enum.auto()

@classmethod
def is_valid_value(cls, value: str) -> bool:
# make CONSTRUCTOR visibility not available to the user
# (although as a design note - maybe `@constructor` should
# indeed be available)
return super().is_valid_value(value) and value != "constructor"


class StateMutability(_StringEnum):
Expand Down
23 changes: 17 additions & 6 deletions vyper/semantics/analysis/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def validate_semantics_r(
Analyze a Vyper module AST node, add all module-level objects to the
namespace, type-check/validate semantics and annotate with type and analysis info
"""
if "type" in module_ast._metadata:
# we don't need to analyse again, skip out
assert isinstance(module_ast._metadata["type"], ModuleT)
return module_ast._metadata["type"]

validate_literal_nodes(module_ast)

# validate semantics and annotate AST with type/semantics information
Expand Down Expand Up @@ -121,11 +126,8 @@ def __init__(
def analyze(self) -> ModuleT:
# generate a `ModuleT` from the top-level node
# note: also validates unique method ids
if "type" in self.ast._metadata:
assert isinstance(self.ast._metadata["type"], ModuleT)
# we don't need to analyse again, skip out
self.module_t = self.ast._metadata["type"]
return self.module_t

assert "type" not in self.ast._metadata

to_visit = self.ast.body.copy()

Expand Down Expand Up @@ -195,7 +197,8 @@ def analyze_call_graph(self):
# we just want to be able to construct the call graph.
continue

if isinstance(call_t, ContractFunctionT) and call_t.is_internal:
if isinstance(call_t, ContractFunctionT) and (call_t.is_internal or call_t.is_constructor):

fn_t.called_functions.add(call_t)

for func in function_defs:
Expand All @@ -222,6 +225,14 @@ def visit_ImplementsDecl(self, node):

type_.validate_implements(node)

def visit_UsesDecl(self, node):
# XXX: implement
pass

def visit_InitializesDecl(self, node):
# XXX: implement
pass
Fixed Show fixed Hide fixed

def visit_VariableDecl(self, node):
name = node.get("target.id")
if name is None:
Expand Down
29 changes: 18 additions & 11 deletions vyper/semantics/types/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ def from_vyi(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
"function body in an interface can only be ...!", funcdef
)

assert function_visibility is not None # mypy hint

return cls(
funcdef.name,
positional_args,
Expand Down Expand Up @@ -315,13 +317,15 @@ def from_FunctionDef(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
)

if funcdef.name == "__init__":
if (
state_mutability in (StateMutability.PURE, StateMutability.VIEW)
or function_visibility == FunctionVisibility.INTERNAL
):
if state_mutability in (StateMutability.PURE, StateMutability.VIEW):
raise FunctionDeclarationException(
"Constructor cannot be marked as `@pure` or `@view`", funcdef
)
if function_visibility is not None:
raise FunctionDeclarationException(
"Constructor cannot be marked as `@pure`, `@view` or `@internal`", funcdef
"Constructor cannot be marked as `@internal` or `@external`", funcdef
)
function_visibility = FunctionVisibility.CONSTRUCTOR
if return_type is not None:
raise FunctionDeclarationException(
"Constructor may not have a return type", funcdef.returns
Expand All @@ -333,6 +337,9 @@ def from_FunctionDef(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
"Constructor may not use default arguments", funcdef.args.defaults[0]
)

# sanity check
assert function_visibility is not None

return cls(
funcdef.name,
positional_args,
Expand Down Expand Up @@ -456,6 +463,10 @@ def is_external(self) -> bool:
def is_internal(self) -> bool:
return self.visibility == FunctionVisibility.INTERNAL

@property
def is_constructor(self) -> bool:
return self.visibility == FunctionVisibility.CONSTRUCTOR

@property
def is_mutable(self) -> bool:
return self.mutability > StateMutability.VIEW
Expand All @@ -464,10 +475,6 @@ def is_mutable(self) -> bool:
def is_payable(self) -> bool:
return self.mutability == StateMutability.PAYABLE

@property
def is_constructor(self) -> bool:
return self.name == "__init__"

@property
def is_fallback(self) -> bool:
return self.name == "__default__"
Expand Down Expand Up @@ -601,7 +608,7 @@ def _parse_return_type(funcdef: vy_ast.FunctionDef) -> Optional[VyperType]:

def _parse_decorators(
funcdef: vy_ast.FunctionDef,
) -> tuple[FunctionVisibility, StateMutability, Optional[str]]:
) -> tuple[Optional[FunctionVisibility], StateMutability, Optional[str]]:
function_visibility = None
state_mutability = None
nonreentrant_key = None
Expand Down Expand Up @@ -656,7 +663,7 @@ def _parse_decorators(
else:
raise StructureException("Bad decorator syntax", decorator)

if function_visibility is None:
if function_visibility is None and funcdef.name != "__init__":
raise FunctionDeclarationException(
f"Visibility must be set to one of: {', '.join(FunctionVisibility.values())}", funcdef
)
Expand Down
Loading