diff --git a/vyper/builtins/_signatures.py b/vyper/builtins/_signatures.py index 1b37f4c556..34af98f996 100644 --- a/vyper/builtins/_signatures.py +++ b/vyper/builtins/_signatures.py @@ -1,9 +1,84 @@ import functools +from typing import Any, Optional, Union +from vyper import ast as vy_ast +from vyper.ast.validation import validate_call_args from vyper.codegen.expr import Expr from vyper.codegen.ir_node import IRnode -from vyper.exceptions import CompilerPanic -from vyper.semantics.types import TYPE_T, VyperType +from vyper.exceptions import CompilerPanic, TypeMismatch +from vyper.semantics.analysis.utils import get_exact_type_from_node, validate_expected_type +from vyper.semantics.types.base import TYPE_T, KwargSettings, VyperType +from vyper.semantics.types.utils import type_from_annotation + + +class BuiltinFunctionT(VyperType): + _has_varargs = False + _inputs: list[tuple[str, Any]] = [] + _kwargs: dict[str, KwargSettings] = {} + _return_type: Optional[VyperType] = None + + # helper function to deal with TYPE_DEFINITIONs + def _validate_single(self, arg: vy_ast.VyperNode, expected_type: VyperType) -> None: + # TODO using "TYPE_DEFINITION" is a kludge in derived classes, + # refactor me. + if expected_type == "TYPE_DEFINITION": + # try to parse the type - call type_from_annotation + # for its side effects (will throw if is not a type) + type_from_annotation(arg) + else: + validate_expected_type(arg, expected_type) + + def _validate_arg_types(self, node: vy_ast.Call) -> None: + num_args = len(self._inputs) # the number of args the signature indicates + + expect_num_args: Union[int, tuple] = num_args + if self._has_varargs: + # note special meaning for -1 in validate_call_args API + expect_num_args = (num_args, -1) + + validate_call_args(node, expect_num_args, list(self._kwargs.keys())) + + for arg, (_, expected) in zip(node.args, self._inputs): + self._validate_single(arg, expected) + + for kwarg in node.keywords: + kwarg_settings = self._kwargs[kwarg.arg] + if kwarg_settings.require_literal and not isinstance(kwarg.value, vy_ast.Constant): + raise TypeMismatch("Value for kwarg must be a literal", kwarg.value) + self._validate_single(kwarg.value, kwarg_settings.typ) + + # typecheck varargs. we don't have type info from the signature, + # so ensure that the types of the args can be inferred exactly. + varargs = node.args[num_args:] + if len(varargs) > 0: + assert self._has_varargs # double check validate_call_args + for arg in varargs: + # call get_exact_type_from_node for its side effects - + # ensures the type can be inferred exactly. + get_exact_type_from_node(arg) + + def fetch_call_return(self, node: vy_ast.Call) -> Optional[VyperType]: + self._validate_arg_types(node) + + return self._return_type + + def infer_arg_types(self, node: vy_ast.Call) -> list[VyperType]: + self._validate_arg_types(node) + ret = [expected for (_, expected) in self._inputs] + + # handle varargs. + n_known_args = len(self._inputs) + varargs = node.args[n_known_args:] + if len(varargs) > 0: + assert self._has_varargs + ret.extend(get_exact_type_from_node(arg) for arg in varargs) + return ret + + def infer_kwarg_types(self, node: vy_ast.Call) -> dict[str, VyperType]: + return {i.arg: self._kwargs[i.arg].typ for i in node.keywords} + + def __repr__(self): + return f"(builtin) {self._id}" def process_arg(arg, expected_arg_type, context): diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py index 88c030e7bd..b2d817ec5c 100644 --- a/vyper/builtins/functions.py +++ b/vyper/builtins/functions.py @@ -74,7 +74,6 @@ TupleT, ) from vyper.semantics.types.bytestrings import _BytestringT -from vyper.semantics.types.function import BuiltinFunctionT from vyper.semantics.types.shortcuts import ( BYTES4_T, BYTES32_T, @@ -99,7 +98,7 @@ ) from ._convert import convert -from ._signatures import process_inputs +from ._signatures import BuiltinFunctionT, process_inputs SHA256_ADDRESS = 2 SHA256_BASE_GAS = 60 diff --git a/vyper/semantics/types/function.py b/vyper/semantics/types/function.py index 127bf395b2..77b9efb13d 100644 --- a/vyper/semantics/types/function.py +++ b/vyper/semantics/types/function.py @@ -2,7 +2,7 @@ import warnings from dataclasses import dataclass from functools import cached_property -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple from vyper import ast as vy_ast from vyper.ast.identifiers import validate_identifier @@ -15,14 +15,9 @@ InvalidType, StateAccessViolation, StructureException, - TypeMismatch, ) from vyper.semantics.analysis.base import FunctionVisibility, StateMutability, StorageSlot -from vyper.semantics.analysis.utils import ( - check_kwargable, - get_exact_type_from_node, - validate_expected_type, -) +from vyper.semantics.analysis.utils import check_kwargable, validate_expected_type from vyper.semantics.data_locations import DataLocation from vyper.semantics.types.base import KwargSettings, VyperType from vyper.semantics.types.primitives import BoolT @@ -640,76 +635,6 @@ def fetch_call_return(self, node: vy_ast.Call) -> Optional[VyperType]: return self.return_type -class BuiltinFunctionT(VyperType): - _has_varargs = False - _inputs: list[tuple[str, Any]] = [] - _kwargs: Dict[str, KwargSettings] = {} - _return_type: Optional[VyperType] = None - - # helper function to deal with TYPE_DEFINITIONs - def _validate_single(self, arg: vy_ast.VyperNode, expected_type: VyperType) -> None: - # TODO using "TYPE_DEFINITION" is a kludge in derived classes, - # refactor me. - if expected_type == "TYPE_DEFINITION": - # try to parse the type - call type_from_annotation - # for its side effects (will throw if is not a type) - type_from_annotation(arg) - else: - validate_expected_type(arg, expected_type) - - def _validate_arg_types(self, node: vy_ast.Call) -> None: - num_args = len(self._inputs) # the number of args the signature indicates - - expect_num_args: Union[int, tuple] = num_args - if self._has_varargs: - # note special meaning for -1 in validate_call_args API - expect_num_args = (num_args, -1) - - validate_call_args(node, expect_num_args, list(self._kwargs.keys())) - - for arg, (_, expected) in zip(node.args, self._inputs): - self._validate_single(arg, expected) - - for kwarg in node.keywords: - kwarg_settings = self._kwargs[kwarg.arg] - if kwarg_settings.require_literal and not isinstance(kwarg.value, vy_ast.Constant): - raise TypeMismatch("Value for kwarg must be a literal", kwarg.value) - self._validate_single(kwarg.value, kwarg_settings.typ) - - # typecheck varargs. we don't have type info from the signature, - # so ensure that the types of the args can be inferred exactly. - varargs = node.args[num_args:] - if len(varargs) > 0: - assert self._has_varargs # double check validate_call_args - for arg in varargs: - # call get_exact_type_from_node for its side effects - - # ensures the type can be inferred exactly. - get_exact_type_from_node(arg) - - def fetch_call_return(self, node: vy_ast.Call) -> Optional[VyperType]: - self._validate_arg_types(node) - - return self._return_type - - def infer_arg_types(self, node: vy_ast.Call) -> list[VyperType]: - self._validate_arg_types(node) - ret = [expected for (_, expected) in self._inputs] - - # handle varargs. - n_known_args = len(self._inputs) - varargs = node.args[n_known_args:] - if len(varargs) > 0: - assert self._has_varargs - ret.extend(get_exact_type_from_node(arg) for arg in varargs) - return ret - - def infer_kwarg_types(self, node: vy_ast.Call) -> dict[str, VyperType]: - return {i.arg: self._kwargs[i.arg].typ for i in node.keywords} - - def __repr__(self): - return f"(builtin) {self._id}" - - def _generate_method_id(name: str, canonical_abi_types: List[str]) -> Dict[str, int]: function_sig = f"{name}({','.join(canonical_abi_types)})" selector = keccak256(function_sig.encode())[:4].hex()