From 0bc4c5611f6b08541f6264890c609c63c7653f33 Mon Sep 17 00:00:00 2001 From: William Grant Date: Tue, 29 Nov 2022 12:12:37 -0500 Subject: [PATCH 1/3] new compiler input class with arg prep functions Pulls function object, overload, and closure info to create a class representing the packet of info the compiler needs in order to perform the compilation. Also moves TypeOf within init to avoid circular imports, and removes some redundant lambdas. --- typed_python/__init__.py | 2 +- typed_python/compiler/compiler_input.py | 110 ++++++++++++++++++ .../compiler/python_to_native_converter.py | 62 +++------- typed_python/compiler/runtime.py | 18 +-- typed_python/compiler/typeof.py | 16 +-- 5 files changed, 147 insertions(+), 61 deletions(-) create mode 100644 typed_python/compiler/compiler_input.py diff --git a/typed_python/__init__.py b/typed_python/__init__.py index d7da9e8a7..9214addca 100644 --- a/typed_python/__init__.py +++ b/typed_python/__init__.py @@ -42,7 +42,6 @@ from typed_python.hash import sha_hash from typed_python.SerializationContext import SerializationContext from typed_python.type_filter import TypeFilter -from typed_python.compiler.typeof import TypeOf from typed_python._types import ( Forward, TupleOf, ListOf, Tuple, NamedTuple, OneOf, ConstDict, SubclassOf, Alternative, Value, serialize, deserialize, serializeStream, deserializeStream, @@ -78,6 +77,7 @@ from typed_python.lib.map import map # noqa from typed_python.lib.pmap import pmap # noqa from typed_python.lib.reduce import reduce # noqa +from typed_python.compiler.typeof import TypeOf # noqa _types.initializeGlobalStatics() diff --git a/typed_python/compiler/compiler_input.py b/typed_python/compiler/compiler_input.py new file mode 100644 index 000000000..e69c55034 --- /dev/null +++ b/typed_python/compiler/compiler_input.py @@ -0,0 +1,110 @@ +from typing import List +from typed_python.compiler.python_object_representation import typedPythonTypeToTypeWrapper +from typed_python.compiler.type_wrappers.python_typed_function_wrapper import ( + PythonTypedFunctionWrapper, CannotBeDetermined, NoReturnTypeSpecified +) +from typed_python.internals import FunctionOverload + + +class CompilerInput: + """ + Represents a parcel of input code and its input types + closure, containing everything the + compiler needs in order to do the compilation. Typed_python supports function overloading, + so everything here is specific to a given 'overload' - a function for a given set of input + types, and inferred output type. + + Args: + function_type: a typed_python.Function _type_. + overload_index: the integer index of the overload we are interested in. + input_wrappers: A list of types (or TypeWrappers) for each input argument. + """ + def __init__(self, function_type, overload_index: int, input_wrappers=None) -> None: + self._overload: FunctionOverload = function_type.overloads[overload_index] + self._closure_type = function_type.ClosureType + self._input_wrappers: List = input_wrappers + self._realized_input_wrappers: List = None + self._return_type = None + self._return_type_calculated = False + + def expand_input_wrappers(self) -> None: + """ + Extend the list of wrappers (representing the input args) using the free variables in + the function closure. + """ + realized_input_wrappers = [] + for closure_var_path in self.closureVarLookups.values(): + realized_input_wrappers.append( + typedPythonTypeToTypeWrapper( + PythonTypedFunctionWrapper.closurePathToCellType(closure_var_path, self.closure_type) + ) + ) + realized_input_wrappers.extend(self.input_wrappers) + self._realized_input_wrappers = realized_input_wrappers + + def compute_return_type(self) -> None: + """Determine the return type, if possible.""" + return_type = PythonTypedFunctionWrapper.computeFunctionOverloadReturnType(self._overload, + self.input_wrappers, + {} + ) + if return_type is CannotBeDetermined: + return_type = object + + elif return_type is NoReturnTypeSpecified: + return_type = None + + self._return_type = return_type + self._return_type_calculated = True + + def install_native_pointer(self, fp, returnType, argumentTypes) -> None: + return self._overload._installNativePointer(fp, returnType, argumentTypes) + + @property + def return_type(self): + if not self._return_type_calculated: + self.compute_return_type() + return self._return_type + + @property + def realized_input_wrappers(self): + return self._realized_input_wrappers + + @property + def input_wrappers(self): + return self._input_wrappers + + @input_wrappers.setter + def input_wrappers(self, wrappers: List): + self._input_wrappers = wrappers + + @property + def closure_type(self): + return self._closure_type + + @property + def args(self): + return self._overload.args + + @property + def name(self): + return self._overload.name + + @property + def functionCode(self): + return self._overload.functionCode + + @property + def realizedGlobals(self): + return self._overload.realizedGlobals + + @property + def functionGlobals(self): + return self._overload.functionGlobals + + @property + def funcGlobalsInCells(self): + return self._overload.funcGlobalsInCells + + @property + def closureVarLookups(self): + return self._overload.closureVarLookups diff --git a/typed_python/compiler/python_to_native_converter.py b/typed_python/compiler/python_to_native_converter.py index 561000b3d..53c656d85 100644 --- a/typed_python/compiler/python_to_native_converter.py +++ b/typed_python/compiler/python_to_native_converter.py @@ -19,24 +19,18 @@ from types import ModuleType import typed_python.python_ast as python_ast import typed_python._types as _types -import typed_python.compiler import typed_python.compiler.native_ast as native_ast from typed_python.compiler.native_function_pointer import NativeFunctionPointer from sortedcontainers import SortedSet +from typed_python.compiler.compiler_input import CompilerInput from typed_python.compiler.directed_graph import DirectedGraph from typed_python.compiler.type_wrappers.wrapper import Wrapper from typed_python.compiler.type_wrappers.class_wrapper import ClassWrapper from typed_python.compiler.python_object_representation import typedPythonTypeToTypeWrapper from typed_python.compiler.function_conversion_context import FunctionConversionContext, FunctionOutput, FunctionYield from typed_python.compiler.native_function_conversion_context import NativeFunctionConversionContext -from typed_python.compiler.type_wrappers.python_typed_function_wrapper import ( - PythonTypedFunctionWrapper, CannotBeDetermined, NoReturnTypeSpecified -) from typed_python.compiler.typed_call_target import TypedCallTarget -typeWrapper = lambda t: typed_python.compiler.python_object_representation.typedPythonTypeToTypeWrapper(t) - - VALIDATE_FUNCTION_DEFINITIONS_STABLE = False @@ -343,8 +337,8 @@ def defineNativeFunction(self, name, identity, input_types, output_type, generat returns a TypedCallTarget. 'generatingFunction' may call this recursively if it wants. """ - output_type = typeWrapper(output_type) - input_types = [typeWrapper(x) for x in input_types] + output_type = typedPythonTypeToTypeWrapper(output_type) + input_types = [typedPythonTypeToTypeWrapper(x) for x in input_types] identity = ( Hash.from_integer(2) + @@ -419,7 +413,7 @@ def generator(context, out, *args): "demasquerade_" + callTarget.name, ("demasquerade", callTarget.name), callTarget.input_types, - typeWrapper(callTarget.output_type.interpreterTypeRepresentation), + typedPythonTypeToTypeWrapper(callTarget.output_type.interpreterTypeRepresentation), generator ) @@ -593,7 +587,7 @@ def compileSingleClassDispatch(self, interfaceClass, implementingClass, slotInde _types.installClassMethodDispatch(interfaceClass, implementingClass, slotIndex, fp.fp) def compileClassDestructor(self, cls): - typedCallTarget = typeWrapper(cls).compileDestructor(self) + typedCallTarget = typedPythonTypeToTypeWrapper(cls).compileDestructor(self) assert typedCallTarget is not None @@ -620,43 +614,21 @@ def functionPointerByName(self, linkerName) -> NativeFunctionPointer: # compiler cache has all the pointers. return self.compilerCache.function_pointer_by_name(linkerName) - def convertTypedFunctionCall(self, functionType, overloadIx, inputWrappers, assertIsRoot=False): - overload = functionType.overloads[overloadIx] - - realizedInputWrappers = [] - - closureType = functionType.ClosureType - - for closureVarName, closureVarPath in overload.closureVarLookups.items(): - realizedInputWrappers.append( - typeWrapper( - PythonTypedFunctionWrapper.closurePathToCellType(closureVarPath, closureType) - ) - ) - - realizedInputWrappers.extend(inputWrappers) - - returnType = PythonTypedFunctionWrapper.computeFunctionOverloadReturnType( - overload, - inputWrappers, - {} - ) - - if returnType is CannotBeDetermined: - returnType = object + def convertTypedFunctionCall(self, compiler_input: CompilerInput, assertIsRoot=False): + """Expand the input wrappers using the closure types, find the return type, and convert.""" + compiler_input.expand_input_wrappers() - if returnType is NoReturnTypeSpecified: - returnType = None + compiler_input.compute_return_type() return self.convert( - overload.name, - overload.functionCode, - overload.realizedGlobals, - overload.functionGlobals, - overload.funcGlobalsInCells, - list(overload.closureVarLookups), - realizedInputWrappers, - returnType, + compiler_input.name, + compiler_input.functionCode, + compiler_input.realizedGlobals, + compiler_input.functionGlobals, + compiler_input.funcGlobalsInCells, + list(compiler_input.closureVarLookups), + compiler_input.realized_input_wrappers, + compiler_input.return_type, assertIsRoot=assertIsRoot ) diff --git a/typed_python/compiler/runtime.py b/typed_python/compiler/runtime.py index ba5011ac2..d62f7f433 100644 --- a/typed_python/compiler/runtime.py +++ b/typed_python/compiler/runtime.py @@ -22,6 +22,7 @@ from typed_python.compiler.runtime_lock import runtimeLock from typed_python.compiler.conversion_level import ConversionLevel from typed_python.compiler.compiler_cache import CompilerCache +from typed_python.compiler.compiler_input import CompilerInput from typed_python.type_function import TypeFunction from typed_python.compiler.type_wrappers.typed_tuple_masquerading_as_tuple_wrapper import TypedTupleMasqueradingAsTuple from typed_python.compiler.type_wrappers.named_tuple_masquerading_as_dict_wrapper import NamedTupleMasqueradingAsDict @@ -261,9 +262,11 @@ def compileFunctionOverload(self, functionType, overloadIx, arguments, arguments None if it is not possible to match this overload with these arguments or a TypedCallTarget. """ - overload = functionType.overloads[overloadIx] - assert len(arguments) == len(overload.args) + # generate the parcel of code corresponding to an input to the compiler + compiler_input = CompilerInput(functionType, overloadIx) + + assert len(arguments) == len(compiler_input.args) try: t0 = time.time() @@ -276,19 +279,20 @@ def compileFunctionOverload(self, functionType, overloadIx, arguments, arguments for i in range(len(arguments)): inputWrappers.append( - self.pickSpecializationTypeFor(overload.args[i], arguments[i], argumentsAreTypes) + self.pickSpecializationTypeFor(compiler_input.args[i], arguments[i], argumentsAreTypes) ) if any(x is None for x in inputWrappers): # this signature is unmatchable with these arguments. return None + # generate the input packet. + compiler_input.input_wrappers = inputWrappers + self.timesCompiled += 1 callTarget = self.converter.convertTypedFunctionCall( - functionType, - overloadIx, - inputWrappers, + compiler_input, assertIsRoot=True ) @@ -304,7 +308,7 @@ def compileFunctionOverload(self, functionType, overloadIx, arguments, arguments fp = self.converter.functionPointerByName(wrappingCallTargetName) - overload._installNativePointer( + compiler_input.install_native_pointer( fp.fp, callTarget.output_type.typeRepresentation if callTarget.output_type is not None else type(None), [i.typeRepresentation for i in callTarget.input_types] diff --git a/typed_python/compiler/typeof.py b/typed_python/compiler/typeof.py index 270f13ff8..d47209ab6 100644 --- a/typed_python/compiler/typeof.py +++ b/typed_python/compiler/typeof.py @@ -1,8 +1,8 @@ import typed_python -from typed_python.compiler.type_wrappers.type_sets import SubclassOf, Either - -typeWrapper = lambda t: typed_python.compiler.python_to_native_converter.typedPythonTypeToTypeWrapper(t) +from typed_python.compiler.compiler_input import CompilerInput +from typed_python.compiler.type_wrappers.type_sets import SubclassOf, Either +from typed_python.compiler.python_object_representation import typedPythonTypeToTypeWrapper class TypeOf: @@ -47,8 +47,8 @@ def __call__(self, *args, **kwargs): def resultTypeForCall(self, argTypes, kwargTypes): funcObj = typed_python._types.prepareArgumentToBePassedToCompiler(self.F) - argTypes = [typeWrapper(a) for a in argTypes] - kwargTypes = {k: typeWrapper(v) for k, v in kwargTypes.items()} + argTypes = [typedPythonTypeToTypeWrapper(a) for a in argTypes] + kwargTypes = {k: typedPythonTypeToTypeWrapper(v) for k, v in kwargTypes.items()} overload = funcObj.overloads[0] @@ -61,10 +61,10 @@ def resultTypeForCall(self, argTypes, kwargTypes): converter = typed_python.compiler.runtime.Runtime.singleton().converter + compiler_input = CompilerInput(funcObj, overload_index=0, input_wrappers=argumentSignature) + callTarget = converter.convertTypedFunctionCall( - type(funcObj), - 0, - argumentSignature, + compiler_input, assertIsRoot=False ) From a4b9ca8570bb671d9106e010b9681c821c5a28f2 Mon Sep 17 00:00:00 2001 From: Augustus Lonergan Date: Thu, 1 Dec 2022 22:18:54 +0000 Subject: [PATCH 2/3] style changes --- typed_python/compiler/compiler_input.py | 205 ++++++++++++------ .../compiler/python_to_native_converter.py | 4 - typed_python/compiler/runtime.py | 84 +------ 3 files changed, 150 insertions(+), 143 deletions(-) diff --git a/typed_python/compiler/compiler_input.py b/typed_python/compiler/compiler_input.py index e69c55034..acf45eced 100644 --- a/typed_python/compiler/compiler_input.py +++ b/typed_python/compiler/compiler_input.py @@ -1,9 +1,25 @@ -from typing import List +import types + +import typed_python from typed_python.compiler.python_object_representation import typedPythonTypeToTypeWrapper from typed_python.compiler.type_wrappers.python_typed_function_wrapper import ( - PythonTypedFunctionWrapper, CannotBeDetermined, NoReturnTypeSpecified + PythonTypedFunctionWrapper, + CannotBeDetermined, + NoReturnTypeSpecified, +) +from typed_python.compiler.type_wrappers.typed_tuple_masquerading_as_tuple_wrapper import ( + TypedTupleMasqueradingAsTuple, +) +from typed_python.compiler.type_wrappers.named_tuple_masquerading_as_dict_wrapper import ( + NamedTupleMasqueradingAsDict, ) -from typed_python.internals import FunctionOverload +from typed_python.compiler.conversion_level import ConversionLevel +from typed_python import Function, Value +from typed_python.type_function import TypeFunction + +import typed_python.compiler.python_to_native_converter as python_to_native_converter + +typeWrapper = lambda t: python_to_native_converter.typedPythonTypeToTypeWrapper(t) class CompilerInput: @@ -12,75 +28,32 @@ class CompilerInput: compiler needs in order to do the compilation. Typed_python supports function overloading, so everything here is specific to a given 'overload' - a function for a given set of input types, and inferred output type. - - Args: - function_type: a typed_python.Function _type_. - overload_index: the integer index of the overload we are interested in. - input_wrappers: A list of types (or TypeWrappers) for each input argument. """ - def __init__(self, function_type, overload_index: int, input_wrappers=None) -> None: - self._overload: FunctionOverload = function_type.overloads[overload_index] - self._closure_type = function_type.ClosureType - self._input_wrappers: List = input_wrappers - self._realized_input_wrappers: List = None + def __init__(self, overload, closure_type, input_wrappers): + self._overload = overload + self.closure_type = closure_type + self._input_wrappers = input_wrappers + + # cached properties + self._realized_input_wrappers = None self._return_type = None self._return_type_calculated = False - def expand_input_wrappers(self) -> None: - """ - Extend the list of wrappers (representing the input args) using the free variables in - the function closure. - """ - realized_input_wrappers = [] - for closure_var_path in self.closureVarLookups.values(): - realized_input_wrappers.append( - typedPythonTypeToTypeWrapper( - PythonTypedFunctionWrapper.closurePathToCellType(closure_var_path, self.closure_type) - ) - ) - realized_input_wrappers.extend(self.input_wrappers) - self._realized_input_wrappers = realized_input_wrappers - - def compute_return_type(self) -> None: - """Determine the return type, if possible.""" - return_type = PythonTypedFunctionWrapper.computeFunctionOverloadReturnType(self._overload, - self.input_wrappers, - {} - ) - if return_type is CannotBeDetermined: - return_type = object - - elif return_type is NoReturnTypeSpecified: - return_type = None - - self._return_type = return_type - self._return_type_calculated = True + @property + def realized_input_wrappers(self): + if self._realized_input_wrappers is None: + self._realized_input_wrappers = self._compute_realized_input_wrappers() + assert self._realized_input_wrappers is not None - def install_native_pointer(self, fp, returnType, argumentTypes) -> None: - return self._overload._installNativePointer(fp, returnType, argumentTypes) + return self._realized_input_wrappers @property def return_type(self): if not self._return_type_calculated: - self.compute_return_type() + self._return_type = self._compute_return_type() + self._return_type_calculated = True return self._return_type - @property - def realized_input_wrappers(self): - return self._realized_input_wrappers - - @property - def input_wrappers(self): - return self._input_wrappers - - @input_wrappers.setter - def input_wrappers(self, wrappers: List): - self._input_wrappers = wrappers - - @property - def closure_type(self): - return self._closure_type - @property def args(self): return self._overload.args @@ -108,3 +81,113 @@ def funcGlobalsInCells(self): @property def closureVarLookups(self): return self._overload.closureVarLookups + + def _compute_realized_input_wrappers(self) -> None: + """ + Extend the list of wrappers (representing the input args) using the free variables in + the function closure. + """ + res = [] + for closure_var_path in self.closureVarLookups.values(): + res.append( + typedPythonTypeToTypeWrapper( + PythonTypedFunctionWrapper.closurePathToCellType(closure_var_path, self.closure_type) + ) + ) + res.extend(self._input_wrappers) + return res + + def _compute_return_type(self) -> None: + """Determine the return type, if possible.""" + res = PythonTypedFunctionWrapper.computeFunctionOverloadReturnType( + self._overload, self._input_wrappers, {} + ) + + if res is CannotBeDetermined: + res = object + + elif res is NoReturnTypeSpecified: + res = None + + return res + + def install_native_pointer(self, fp, returnType, argumentTypes) -> None: + return self._overload._installNativePointer(fp, returnType, argumentTypes) + + @staticmethod + def make(functionType, overloadIx, arguments, argumentsAreTypes): + overload = functionType.overloads[overloadIx] + + if len(arguments) != len(overload.args): + raise Exception( + "CompilerInput mismatch: overload has %s args, but we were given " + "%s arguments" % (len(overload.args), len(arguments)) + ) + + inputWrappers = [] + + for i in range(len(arguments)): + specialization = pickSpecializationTypeFor( + overload.args[i], arguments[i], argumentsAreTypes + ) + + if specialization is None: + return None + + inputWrappers.append(specialization) + + return CompilerInput(overload, functionType.ClosureType, inputWrappers) + + +def pickSpecializationTypeFor(overloadArg, argValue, argumentsAreTypes): + """Compute the typeWrapper we'll use for this particular argument based on 'argValue'. + + Args: + overloadArg - the internals.FunctionOverloadArg instance representing this argument. + This tells us whether we're dealing with a normal positional/keyword argument or + a *arg / **kwarg, where the typeFilter applies to the items of the tuple but + not the tuple itself. + argValue - the value being passed for this argument. If 'argumentsAreTypes' is true, + then this is the actual type, not the value. + + Returns: + the Wrapper or type instance to use for this argument. + """ + if not argumentsAreTypes: + if overloadArg.isStarArg: + argType = TypedTupleMasqueradingAsTuple( + typed_python.Tuple(*[passingTypeForValue(v) for v in argValue]) + ) + elif overloadArg.isKwarg: + argType = NamedTupleMasqueradingAsDict( + typed_python.NamedTuple( + **{k: passingTypeForValue(v) for k, v in argValue.items()} + ) + ) + else: + argType = typeWrapper(passingTypeForValue(argValue)) + else: + argType = typeWrapper(argValue) + + resType = PythonTypedFunctionWrapper.pickSpecializationTypeFor(overloadArg, argType) + + if argType.can_convert_to_type(resType, ConversionLevel.Implicit) is False: + return None + + if (overloadArg.isStarArg or overloadArg.isKwarg) and resType != argType: + return None + + return resType + + +def passingTypeForValue(arg): + if isinstance(arg, types.FunctionType): + return type(Function(arg)) + + elif isinstance(arg, type) and issubclass(arg, TypeFunction) and len(arg.MRO) == 2: + return Value(arg) + + elif isinstance(arg, type): + return Value(arg) + + return type(arg) diff --git a/typed_python/compiler/python_to_native_converter.py b/typed_python/compiler/python_to_native_converter.py index 53c656d85..39a55bcf6 100644 --- a/typed_python/compiler/python_to_native_converter.py +++ b/typed_python/compiler/python_to_native_converter.py @@ -616,10 +616,6 @@ def functionPointerByName(self, linkerName) -> NativeFunctionPointer: def convertTypedFunctionCall(self, compiler_input: CompilerInput, assertIsRoot=False): """Expand the input wrappers using the closure types, find the return type, and convert.""" - compiler_input.expand_input_wrappers() - - compiler_input.compute_return_type() - return self.convert( compiler_input.name, compiler_input.functionCode, diff --git a/typed_python/compiler/runtime.py b/typed_python/compiler/runtime.py index d62f7f433..7ff21598e 100644 --- a/typed_python/compiler/runtime.py +++ b/typed_python/compiler/runtime.py @@ -15,17 +15,12 @@ import threading import os import time -import types import typed_python.compiler.python_to_native_converter as python_to_native_converter import typed_python.compiler.llvm_compiler as llvm_compiler import typed_python from typed_python.compiler.runtime_lock import runtimeLock -from typed_python.compiler.conversion_level import ConversionLevel from typed_python.compiler.compiler_cache import CompilerCache -from typed_python.compiler.compiler_input import CompilerInput -from typed_python.type_function import TypeFunction -from typed_python.compiler.type_wrappers.typed_tuple_masquerading_as_tuple_wrapper import TypedTupleMasqueradingAsTuple -from typed_python.compiler.type_wrappers.named_tuple_masquerading_as_dict_wrapper import NamedTupleMasqueradingAsDict +from typed_python.compiler.compiler_input import CompilerInput, typeWrapper from typed_python.compiler.type_wrappers.python_typed_function_wrapper import PythonTypedFunctionWrapper, NoReturnTypeSpecified from typed_python import Function, _types, Value from typed_python.compiler.merge_type_wrappers import mergeTypeWrappers @@ -33,8 +28,6 @@ _singleton = [None] _singletonLock = threading.RLock() -typeWrapper = lambda t: python_to_native_converter.typedPythonTypeToTypeWrapper(t) - _resultTypeCache = {} @@ -193,60 +186,6 @@ def addEventVisitor(self, visitor: RuntimeEventVisitor): def removeEventVisitor(self, visitor: RuntimeEventVisitor): self.converter.removeVisitor(visitor) - @staticmethod - def passingTypeForValue(arg): - if isinstance(arg, types.FunctionType): - return type(Function(arg)) - - elif isinstance(arg, type) and issubclass(arg, TypeFunction) and len(arg.MRO) == 2: - return Value(arg) - - elif isinstance(arg, type): - return Value(arg) - - return type(arg) - - @staticmethod - def pickSpecializationTypeFor(overloadArg, argValue, argumentsAreTypes=False): - """Compute the typeWrapper we'll use for this particular argument based on 'argValue'. - - Args: - overloadArg - the internals.FunctionOverloadArg instance representing this argument. - This tells us whether we're dealing with a normal positional/keyword argument or - a *arg / **kwarg, where the typeFilter applies to the items of the tuple but - not the tuple itself. - argValue - the value being passed for this argument. If 'argumentsAreTypes' is true, - then this is the actual type, not the value. - - Returns: - the Wrapper or type instance to use for this argument. - """ - if not argumentsAreTypes: - if overloadArg.isStarArg: - argType = TypedTupleMasqueradingAsTuple( - typed_python.Tuple(*[Runtime.passingTypeForValue(v) for v in argValue]) - ) - elif overloadArg.isKwarg: - argType = NamedTupleMasqueradingAsDict( - typed_python.NamedTuple( - **{k: Runtime.passingTypeForValue(v) for k, v in argValue.items()} - ) - ) - else: - argType = typeWrapper(Runtime.passingTypeForValue(argValue)) - else: - argType = typeWrapper(argValue) - - resType = PythonTypedFunctionWrapper.pickSpecializationTypeFor(overloadArg, argType) - - if argType.can_convert_to_type(resType, ConversionLevel.Implicit) is False: - return None - - if (overloadArg.isStarArg or overloadArg.isKwarg) and resType != argType: - return None - - return resType - def compileFunctionOverload(self, functionType, overloadIx, arguments, argumentsAreTypes=False): """Attempt to compile typedFunc.overloads[overloadIx]' with the given arguments. @@ -263,11 +202,6 @@ def compileFunctionOverload(self, functionType, overloadIx, arguments, arguments a TypedCallTarget. """ - # generate the parcel of code corresponding to an input to the compiler - compiler_input = CompilerInput(functionType, overloadIx) - - assert len(arguments) == len(compiler_input.args) - try: t0 = time.time() t1 = None @@ -275,20 +209,14 @@ def compileFunctionOverload(self, functionType, overloadIx, arguments, arguments defCount = self.converter.getDefinitionCount() with self.lock: - inputWrappers = [] - - for i in range(len(arguments)): - inputWrappers.append( - self.pickSpecializationTypeFor(compiler_input.args[i], arguments[i], argumentsAreTypes) - ) + # generate the parcel of code corresponding to an input to the compiler + compiler_input = CompilerInput.make( + functionType, overloadIx, arguments, argumentsAreTypes + ) - if any(x is None for x in inputWrappers): - # this signature is unmatchable with these arguments. + if compiler_input is None: return None - # generate the input packet. - compiler_input.input_wrappers = inputWrappers - self.timesCompiled += 1 callTarget = self.converter.convertTypedFunctionCall( From 1c4ed3fdd59ca386fd96b5367111c3da6d59614a Mon Sep 17 00:00:00 2001 From: William Grant Date: Thu, 1 Dec 2022 18:52:21 -0500 Subject: [PATCH 3/3] adjust typeof compilerinput usage + typehints --- typed_python/compiler/compiler_input.py | 18 +++++++++++------- .../compiler/python_to_native_converter.py | 1 - typed_python/compiler/typeof.py | 6 +++++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/typed_python/compiler/compiler_input.py b/typed_python/compiler/compiler_input.py index acf45eced..813c93179 100644 --- a/typed_python/compiler/compiler_input.py +++ b/typed_python/compiler/compiler_input.py @@ -1,6 +1,8 @@ import types import typed_python + +from typing import List from typed_python.compiler.python_object_representation import typedPythonTypeToTypeWrapper from typed_python.compiler.type_wrappers.python_typed_function_wrapper import ( PythonTypedFunctionWrapper, @@ -15,6 +17,7 @@ ) from typed_python.compiler.conversion_level import ConversionLevel from typed_python import Function, Value +from typed_python.internals import FunctionOverloadArg from typed_python.type_function import TypeFunction import typed_python.compiler.python_to_native_converter as python_to_native_converter @@ -82,7 +85,7 @@ def funcGlobalsInCells(self): def closureVarLookups(self): return self._overload.closureVarLookups - def _compute_realized_input_wrappers(self) -> None: + def _compute_realized_input_wrappers(self) -> List: """ Extend the list of wrappers (representing the input args) using the free variables in the function closure. @@ -97,7 +100,7 @@ def _compute_realized_input_wrappers(self) -> None: res.extend(self._input_wrappers) return res - def _compute_return_type(self) -> None: + def _compute_return_type(self): """Determine the return type, if possible.""" res = PythonTypedFunctionWrapper.computeFunctionOverloadReturnType( self._overload, self._input_wrappers, {} @@ -116,10 +119,11 @@ def install_native_pointer(self, fp, returnType, argumentTypes) -> None: @staticmethod def make(functionType, overloadIx, arguments, argumentsAreTypes): + """Generate a CompilerInput packet for a given overload + input argument.""" overload = functionType.overloads[overloadIx] if len(arguments) != len(overload.args): - raise Exception( + raise ValueError( "CompilerInput mismatch: overload has %s args, but we were given " "%s arguments" % (len(overload.args), len(arguments)) ) @@ -127,7 +131,7 @@ def make(functionType, overloadIx, arguments, argumentsAreTypes): inputWrappers = [] for i in range(len(arguments)): - specialization = pickSpecializationTypeFor( + specialization = pick_specialization_type_for( overload.args[i], arguments[i], argumentsAreTypes ) @@ -139,15 +143,15 @@ def make(functionType, overloadIx, arguments, argumentsAreTypes): return CompilerInput(overload, functionType.ClosureType, inputWrappers) -def pickSpecializationTypeFor(overloadArg, argValue, argumentsAreTypes): +def pick_specialization_type_for(overloadArg: FunctionOverloadArg, argValue, argumentsAreTypes): """Compute the typeWrapper we'll use for this particular argument based on 'argValue'. Args: - overloadArg - the internals.FunctionOverloadArg instance representing this argument. + overloadArg: the internals.FunctionOverloadArg instance representing this argument. This tells us whether we're dealing with a normal positional/keyword argument or a *arg / **kwarg, where the typeFilter applies to the items of the tuple but not the tuple itself. - argValue - the value being passed for this argument. If 'argumentsAreTypes' is true, + argValue: the value being passed for this argument. If 'argumentsAreTypes' is true, then this is the actual type, not the value. Returns: diff --git a/typed_python/compiler/python_to_native_converter.py b/typed_python/compiler/python_to_native_converter.py index 39a55bcf6..0119fa332 100644 --- a/typed_python/compiler/python_to_native_converter.py +++ b/typed_python/compiler/python_to_native_converter.py @@ -615,7 +615,6 @@ def functionPointerByName(self, linkerName) -> NativeFunctionPointer: return self.compilerCache.function_pointer_by_name(linkerName) def convertTypedFunctionCall(self, compiler_input: CompilerInput, assertIsRoot=False): - """Expand the input wrappers using the closure types, find the return type, and convert.""" return self.convert( compiler_input.name, compiler_input.functionCode, diff --git a/typed_python/compiler/typeof.py b/typed_python/compiler/typeof.py index d47209ab6..9343d13a8 100644 --- a/typed_python/compiler/typeof.py +++ b/typed_python/compiler/typeof.py @@ -61,7 +61,11 @@ def resultTypeForCall(self, argTypes, kwargTypes): converter = typed_python.compiler.runtime.Runtime.singleton().converter - compiler_input = CompilerInput(funcObj, overload_index=0, input_wrappers=argumentSignature) + compiler_input = CompilerInput.make(funcObj, + overloadIx=0, + arguments=argumentSignature, + argumentsAreTypes=True + ) callTarget = converter.convertTypedFunctionCall( compiler_input,