Skip to content

Commit

Permalink
refactor: move validation within IR, and minor refactoring thereof
Browse files Browse the repository at this point in the history
  • Loading branch information
achidlow committed Mar 21, 2024
1 parent af171cf commit db043f2
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 74 deletions.
2 changes: 0 additions & 2 deletions src/puya/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
LogicSignature,
ModuleArtifact,
)
from puya.ir_validation.main import validate_module_artifact
from puya.mir.main import program_ir_to_mir
from puya.models import CompilationArtifact, CompiledContract, CompiledLogicSignature
from puya.options import PuyaOptions
Expand Down Expand Up @@ -167,7 +166,6 @@ def module_irs_to_teal(
artifact_ir = optimize_and_destructure_ir(
context, artifact_ir, artifact_ir_base_path
)
validate_module_artifact(context, artifact_ir)

match artifact_ir:
case ContractIR() as contract:
Expand Down
6 changes: 5 additions & 1 deletion src/puya/ir/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from puya.ir.to_text_visitor import output_artifact_ir_to_path
from puya.ir.types_ import wtype_to_avm_type, wtype_to_avm_types
from puya.ir.utils import format_tuple_index
from puya.ir.validation.main import validate_module_artifact
from puya.models import (
ARC4Method,
ARC4MethodConfig,
Expand Down Expand Up @@ -94,7 +95,6 @@ def build_module_irs(
with ctx.log_exceptions():
logic_sig_ir = _build_logic_sig_ir(ctx, logic_signature)
artifacts.append(logic_sig_ir)

return result


Expand All @@ -118,6 +118,10 @@ def optimize_and_destructure_ir(
output_artifact_ir_to_path(
artifact_ir, artifact_ir_base_path.with_suffix(".destructured.ir")
)
# re-run validation post everything, in case we've accidentally inserted something,
# and in particular post subroutine removal, because some things that are "linked"
# are not necessarily used from the current artifact
validate_module_artifact(context, artifact_ir)
return artifact_ir


Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from puya.context import CompileContext
from puya.ir.models import ModuleArtifact
from puya.ir_validation.min_avm_version_validator import MinAvmVersionValidator
from puya.ir_validation.op_run_mode_validator import OpRunModeValidator
from puya.ir.validation.min_avm_version_validator import MinAvmVersionValidator
from puya.ir.validation.op_run_mode_validator import OpRunModeValidator


def validate_module_artifact(context: CompileContext, artifact: ModuleArtifact) -> None:
OpRunModeValidator.validate(context, artifact)
OpRunModeValidator.validate(artifact)
MinAvmVersionValidator.validate(context, artifact)
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import typing

import structlog

from puya.context import CompileContext
from puya.ir.models import Contract, Intrinsic, LogicSignature, ModuleArtifact
from puya.ir.visitor import IRTraverser

logger = structlog.get_logger(__name__)


class MinAvmVersionValidator(IRTraverser):
def __init__(self, context: CompileContext) -> None:
self.context = context
def __init__(self, target_avm_version: int) -> None:
self.target_avm_version = target_avm_version

@staticmethod
def validate(context: CompileContext, artifact: ModuleArtifact) -> None:
validator = MinAvmVersionValidator(context)
validator = MinAvmVersionValidator(context.options.target_avm_version)

match artifact:
case LogicSignature() as l_sig:
validator.validate_logic_sig(l_sig)
case Contract() as contract:
validator.validate_contract(contract)
case _:
typing.assert_never(artifact)

def validate_logic_sig(self, logic_sig: LogicSignature) -> None:
for sub in logic_sig.program.all_subroutines:
Expand All @@ -27,10 +35,10 @@ def validate_contract(self, contract: Contract) -> None:
self.visit_all_blocks(sub.body)

def visit_intrinsic_op(self, intrinsic: Intrinsic) -> None:
if intrinsic.op.min_avm_version > self.context.options.target_avm_version:
self.context.errors.warning(
f"Opcode {intrinsic.op} requires a min avm version of "
f"{intrinsic.op.min_avm_version} but the target avm version is"
f" {self.context.options.target_avm_version}",
if intrinsic.op.min_avm_version > self.target_avm_version:
logger.warning(
f"Opcode {intrinsic.op} requires a min AVM version of "
f"{intrinsic.op.min_avm_version} but the target AVM version is"
f" {self.target_avm_version}",
intrinsic.source_location,
)
56 changes: 56 additions & 0 deletions src/puya/ir/validation/op_run_mode_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import typing

import structlog

from puya.ir.avm_ops_models import RunMode
from puya.ir.models import Contract, Intrinsic, LogicSignature, ModuleArtifact
from puya.ir.visitor import IRTraverser

logger = structlog.get_logger(__name__)


class OpRunModeValidator(IRTraverser):
def __init__(self, run_mode: typing.Literal[RunMode.app, RunMode.lsig]) -> None:
self._current_run_mode = run_mode

@classmethod
def validate(cls, artifact: ModuleArtifact) -> None:
match artifact:
case LogicSignature() as l_sig:
cls.validate_logic_sig(l_sig)
case Contract() as contract:
cls.validate_contract(contract)
case _:
typing.assert_never(artifact)

@classmethod
def validate_logic_sig(cls, logic_sig: LogicSignature) -> None:
validator = cls(RunMode.lsig)
for sub in logic_sig.program.all_subroutines:
validator.visit_all_blocks(sub.body)

@classmethod
def validate_contract(cls, contract: Contract) -> None:
validator = cls(RunMode.app)
subs = (sub for program in contract.all_programs() for sub in program.all_subroutines)
for sub in subs:
validator.visit_all_blocks(sub.body)

def visit_intrinsic_op(self, intrinsic: Intrinsic) -> None:
match intrinsic.op_variant.supported_modes:
case RunMode.any:
pass
case RunMode.app:
if self._current_run_mode != RunMode.app:
logger.warning(
f"The operation {intrinsic} is only allowed in smart contracts",
location=intrinsic.source_location,
)
case RunMode.lsig:
if self._current_run_mode != RunMode.lsig:
logger.warning(
f"The operation {intrinsic} is only allowed in logic signatures",
location=intrinsic.source_location,
)
case _:
typing.assert_never(intrinsic.op_variant.supported_modes)
60 changes: 0 additions & 60 deletions src/puya/ir_validation/op_run_mode_validator.py

This file was deleted.

0 comments on commit db043f2

Please sign in to comment.