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

Wrap function overload + closure + input wrappers + return type in a single packet #405

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion typed_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()

Expand Down
197 changes: 197 additions & 0 deletions typed_python/compiler/compiler_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
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,
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.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

typeWrapper = lambda t: python_to_native_converter.typedPythonTypeToTypeWrapper(t)


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.
"""
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

@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

return self._realized_input_wrappers

@property
def return_type(self):
if not self._return_type_calculated:
self._return_type = self._compute_return_type()
self._return_type_calculated = True
return self._return_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

def _compute_realized_input_wrappers(self) -> List:
"""
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):
"""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):
"""Generate a CompilerInput packet for a given overload + input argument."""
overload = functionType.overloads[overloadIx]

if len(arguments) != len(overload.args):
raise ValueError(
"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 = pick_specialization_type_for(
overload.args[i], arguments[i], argumentsAreTypes
)

if specialization is None:
return None

inputWrappers.append(specialization)

return CompilerInput(overload, functionType.ClosureType, inputWrappers)


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.
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)
61 changes: 14 additions & 47 deletions typed_python/compiler/python_to_native_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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) +
Expand Down Expand Up @@ -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
)

Expand Down Expand Up @@ -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

Expand All @@ -620,43 +614,16 @@ 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

if returnType is NoReturnTypeSpecified:
returnType = None

def convertTypedFunctionCall(self, compiler_input: CompilerInput, assertIsRoot=False):
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
)

Expand Down
Loading