Skip to content

Commit decccc1

Browse files
feat[lang]: add @inline decorator
only respected if venom is enabled
1 parent a466dc8 commit decccc1

File tree

5 files changed

+35
-12
lines changed

5 files changed

+35
-12
lines changed

vyper/semantics/types/function.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def __init__(
9797
state_mutability: StateMutability,
9898
from_interface: bool = False,
9999
nonreentrant: bool = False,
100+
inline: bool = False,
100101
ast_def: Optional[vy_ast.VyperNode] = None,
101102
) -> None:
102103
super().__init__()
@@ -109,6 +110,7 @@ def __init__(
109110
self.mutability = state_mutability
110111
self.nonreentrant = nonreentrant
111112
self.from_interface = from_interface
113+
self.inline = inline
112114

113115
# sanity check, nonreentrant used to be Optional[str]
114116
assert isinstance(self.nonreentrant, bool)
@@ -250,6 +252,7 @@ def from_abi(cls, abi: dict) -> "ContractFunctionT":
250252
from_interface=True,
251253
function_visibility=FunctionVisibility.EXTERNAL,
252254
state_mutability=StateMutability.from_abi(abi),
255+
inline=False,
253256
)
254257

255258
@classmethod
@@ -309,6 +312,7 @@ def from_InterfaceDef(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
309312
state_mutability,
310313
from_interface=True,
311314
nonreentrant=False,
315+
inline=False,
312316
ast_def=funcdef,
313317
)
314318

@@ -327,14 +331,14 @@ def from_vyi(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
327331
-------
328332
ContractFunctionT
329333
"""
330-
function_visibility, state_mutability, nonreentrant = _parse_decorators(funcdef)
334+
function_visibility, state_mutability, nonreentrant, inline = _parse_decorators(funcdef)
331335

332-
if nonreentrant:
336+
if nonreentrant or inline:
333337
# TODO: refactor so parse_decorators returns the AST location
334-
decorator = next(d for d in funcdef.decorator_list if d.id == "nonreentrant")
335-
raise FunctionDeclarationException(
336-
"`@nonreentrant` not allowed in interfaces", decorator
338+
decorator = next(
339+
d for d in funcdef.decorator_list if d.id in ("nonreentrant", "inline")
337340
)
341+
raise FunctionDeclarationException(f"`@{d.id}` not allowed in interfaces", decorator)
338342

339343
# it's redundant to specify visibility in vyi - always should be external
340344
if function_visibility is None:
@@ -378,6 +382,7 @@ def from_vyi(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
378382
state_mutability,
379383
from_interface=True,
380384
nonreentrant=nonreentrant,
385+
inline=False,
381386
ast_def=funcdef,
382387
)
383388

@@ -395,7 +400,7 @@ def from_FunctionDef(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
395400
-------
396401
ContractFunctionT
397402
"""
398-
function_visibility, state_mutability, nonreentrant = _parse_decorators(funcdef)
403+
function_visibility, state_mutability, nonreentrant, inline = _parse_decorators(funcdef)
399404

400405
# it's redundant to specify internal visibility - it's implied by not being external
401406
if function_visibility is None:
@@ -453,6 +458,7 @@ def from_FunctionDef(cls, funcdef: vy_ast.FunctionDef) -> "ContractFunctionT":
453458
state_mutability,
454459
from_interface=False,
455460
nonreentrant=nonreentrant,
461+
inline=inline,
456462
ast_def=funcdef,
457463
)
458464

@@ -726,10 +732,11 @@ def _parse_return_type(funcdef: vy_ast.FunctionDef) -> Optional[VyperType]:
726732

727733
def _parse_decorators(
728734
funcdef: vy_ast.FunctionDef,
729-
) -> tuple[Optional[FunctionVisibility], StateMutability, bool]:
735+
) -> tuple[Optional[FunctionVisibility], StateMutability, bool, bool]:
730736
function_visibility = None
731737
state_mutability = None
732738
nonreentrant_node = None
739+
inline_node = None
733740

734741
for decorator in funcdef.decorator_list:
735742
if isinstance(decorator, vy_ast.Call):
@@ -747,6 +754,12 @@ def _parse_decorators(
747754

748755
nonreentrant_node = decorator
749756

757+
elif decorator.get("id") == "inline":
758+
if inline_node is not None:
759+
raise StructureException("inline decorator is already set", inline_node)
760+
761+
inline_node = decorator
762+
750763
elif isinstance(decorator, vy_ast.Name):
751764
if FunctionVisibility.is_valid_value(decorator.id):
752765
if function_visibility is not None:
@@ -786,7 +799,8 @@ def _parse_decorators(
786799
raise StructureException("Cannot use reentrancy guard on pure functions", nonreentrant_node)
787800

788801
nonreentrant = nonreentrant_node is not None
789-
return function_visibility, state_mutability, nonreentrant
802+
inline = inline_node is not None
803+
return function_visibility, state_mutability, nonreentrant, inline
790804

791805

792806
def _parse_args(

vyper/venom/context.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ def add_function(self, fn: IRFunction) -> None:
6060
def remove_function(self, fn: IRFunction) -> None:
6161
del self.functions[fn.name]
6262

63-
def create_function(self, name: str) -> IRFunction:
63+
def create_function(self, name: str, inline: bool = False) -> IRFunction:
6464
label = IRLabel(name, True)
6565
assert label not in self.functions, f"duplicate function {label}"
66-
fn = IRFunction(label, self)
66+
fn = IRFunction(label, self, inline=inline)
6767
self.add_function(fn)
6868
return fn
6969

vyper/venom/function.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@ class IRFunction:
2626
ctx: "IRContext" # type: ignore # noqa: F821
2727
args: list
2828
last_variable: int
29+
inline: bool
2930
_basic_block_dict: dict[str, IRBasicBlock]
3031

3132
# Used during code generation
3233
_ast_source_stack: list[IRnode]
3334
_error_msg_stack: list[str]
3435

35-
def __init__(self, name: IRLabel, ctx: "IRContext" = None) -> None: # type: ignore # noqa: F821
36+
def __init__(self, name: IRLabel, ctx: "IRContext" = None, inline: bool = False) -> None: # type: ignore # noqa: F821
3637
self.ctx = ctx
3738
self.name = name
3839
self.args = []
40+
self.inline = inline
3941
self._basic_block_dict = {}
4042

4143
self.last_variable = 0

vyper/venom/ir_node_to_venom.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,10 @@ def _handle_internal_func(
198198
) -> IRFunction:
199199
global _alloca_table
200200

201-
fn = fn.ctx.create_function(ir.args[0].args[0].value)
201+
func_t = ir.passthrough_metadata["func_t"]
202+
inline = func_t.inline
203+
204+
fn = fn.ctx.create_function(ir.args[0].args[0].value, inline=inline)
202205

203206
bb = fn.get_basic_block()
204207

vyper/venom/passes/function_inliner.py

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ def _select_inline_candidate(self) -> Optional[IRFunction]:
7070
if call_count == 1:
7171
return func
7272

73+
# always inline if it is requested
74+
if func.inline:
75+
return func
76+
7377
# Decide whether to inline based on the optimization level.
7478
if self.optimize == OptimizationLevel.CODESIZE:
7579
continue

0 commit comments

Comments
 (0)