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]: add inline decorator #4481

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
30 changes: 22 additions & 8 deletions vyper/semantics/types/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
state_mutability: StateMutability,
from_interface: bool = False,
nonreentrant: bool = False,
inline: bool = False,
ast_def: Optional[vy_ast.VyperNode] = None,
) -> None:
super().__init__()
Expand All @@ -109,6 +110,7 @@
self.mutability = state_mutability
self.nonreentrant = nonreentrant
self.from_interface = from_interface
self.inline = inline

# sanity check, nonreentrant used to be Optional[str]
assert isinstance(self.nonreentrant, bool)
Expand Down Expand Up @@ -250,6 +252,7 @@
from_interface=True,
function_visibility=FunctionVisibility.EXTERNAL,
state_mutability=StateMutability.from_abi(abi),
inline=False,
)

@classmethod
Expand Down Expand Up @@ -309,6 +312,7 @@
state_mutability,
from_interface=True,
nonreentrant=False,
inline=False,
ast_def=funcdef,
)

Expand All @@ -327,14 +331,14 @@
-------
ContractFunctionT
"""
function_visibility, state_mutability, nonreentrant = _parse_decorators(funcdef)
function_visibility, state_mutability, nonreentrant, inline = _parse_decorators(funcdef)

if nonreentrant:
if nonreentrant or inline:
# TODO: refactor so parse_decorators returns the AST location
decorator = next(d for d in funcdef.decorator_list if d.id == "nonreentrant")
raise FunctionDeclarationException(
"`@nonreentrant` not allowed in interfaces", decorator
decorator = next(

Check warning on line 338 in vyper/semantics/types/function.py

View check run for this annotation

Codecov / codecov/patch

vyper/semantics/types/function.py#L338

Added line #L338 was not covered by tests
d for d in funcdef.decorator_list if d.id in ("nonreentrant", "inline")
)
raise FunctionDeclarationException(f"`@{d.id}` not allowed in interfaces", decorator)

Check warning on line 341 in vyper/semantics/types/function.py

View check run for this annotation

Codecov / codecov/patch

vyper/semantics/types/function.py#L341

Added line #L341 was not covered by tests

# it's redundant to specify visibility in vyi - always should be external
if function_visibility is None:
Expand Down Expand Up @@ -378,6 +382,7 @@
state_mutability,
from_interface=True,
nonreentrant=nonreentrant,
inline=False,
ast_def=funcdef,
)

Expand All @@ -395,7 +400,7 @@
-------
ContractFunctionT
"""
function_visibility, state_mutability, nonreentrant = _parse_decorators(funcdef)
function_visibility, state_mutability, nonreentrant, inline = _parse_decorators(funcdef)

# it's redundant to specify internal visibility - it's implied by not being external
if function_visibility is None:
Expand Down Expand Up @@ -453,6 +458,7 @@
state_mutability,
from_interface=False,
nonreentrant=nonreentrant,
inline=inline,
ast_def=funcdef,
)

Expand Down Expand Up @@ -726,10 +732,11 @@

def _parse_decorators(
funcdef: vy_ast.FunctionDef,
) -> tuple[Optional[FunctionVisibility], StateMutability, bool]:
) -> tuple[Optional[FunctionVisibility], StateMutability, bool, bool]:
function_visibility = None
state_mutability = None
nonreentrant_node = None
inline_node = None

for decorator in funcdef.decorator_list:
if isinstance(decorator, vy_ast.Call):
Expand All @@ -747,6 +754,12 @@

nonreentrant_node = decorator

elif decorator.get("id") == "inline":
if inline_node is not None:
raise StructureException("inline decorator is already set", inline_node)

Check warning on line 759 in vyper/semantics/types/function.py

View check run for this annotation

Codecov / codecov/patch

vyper/semantics/types/function.py#L759

Added line #L759 was not covered by tests

inline_node = decorator

Check warning on line 761 in vyper/semantics/types/function.py

View check run for this annotation

Codecov / codecov/patch

vyper/semantics/types/function.py#L761

Added line #L761 was not covered by tests

elif isinstance(decorator, vy_ast.Name):
if FunctionVisibility.is_valid_value(decorator.id):
if function_visibility is not None:
Expand Down Expand Up @@ -786,7 +799,8 @@
raise StructureException("Cannot use reentrancy guard on pure functions", nonreentrant_node)

nonreentrant = nonreentrant_node is not None
return function_visibility, state_mutability, nonreentrant
inline = inline_node is not None
return function_visibility, state_mutability, nonreentrant, inline


def _parse_args(
Expand Down
4 changes: 2 additions & 2 deletions vyper/venom/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ def add_function(self, fn: IRFunction) -> None:
def remove_function(self, fn: IRFunction) -> None:
del self.functions[fn.name]

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

Expand Down
4 changes: 3 additions & 1 deletion vyper/venom/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ class IRFunction:
ctx: "IRContext" # type: ignore # noqa: F821
args: list
last_variable: int
inline: bool
_basic_block_dict: dict[str, IRBasicBlock]

# Used during code generation
_ast_source_stack: list[IRnode]
_error_msg_stack: list[str]

def __init__(self, name: IRLabel, ctx: "IRContext" = None) -> None: # type: ignore # noqa: F821
def __init__(self, name: IRLabel, ctx: "IRContext" = None, inline: bool = False) -> None: # type: ignore # noqa: F821
self.ctx = ctx
self.name = name
self.args = []
self.inline = inline
self._basic_block_dict = {}

self.last_variable = 0
Expand Down
5 changes: 4 additions & 1 deletion vyper/venom/ir_node_to_venom.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ def _handle_internal_func(
) -> IRFunction:
global _alloca_table

fn = fn.ctx.create_function(ir.args[0].args[0].value)
func_t = ir.passthrough_metadata["func_t"]
inline = func_t.inline

fn = fn.ctx.create_function(ir.args[0].args[0].value, inline=inline)

bb = fn.get_basic_block()

Expand Down
4 changes: 4 additions & 0 deletions vyper/venom/passes/function_inliner.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
if call_count == 1:
return func

# always inline if it is requested
if func.inline:
return func

Check warning on line 75 in vyper/venom/passes/function_inliner.py

View check run for this annotation

Codecov / codecov/patch

vyper/venom/passes/function_inliner.py#L75

Added line #L75 was not covered by tests

# Decide whether to inline based on the optimization level.
if self.optimize == OptimizationLevel.CODESIZE:
continue
Expand Down
Loading