Commit 7bbd22bd authored by Stefan Behnel's avatar Stefan Behnel

move major part of SimplifyCalls transform into type analysis in GeneralCallNode

parent d86ee399
...@@ -4299,6 +4299,7 @@ class GeneralCallNode(CallNode): ...@@ -4299,6 +4299,7 @@ class GeneralCallNode(CallNode):
# keyword_args ExprNode or None Dict of keyword arguments # keyword_args ExprNode or None Dict of keyword arguments
type = py_object_type type = py_object_type
is_simple_call = False
subexprs = ['function', 'positional_args', 'keyword_args'] subexprs = ['function', 'positional_args', 'keyword_args']
...@@ -4324,17 +4325,23 @@ class GeneralCallNode(CallNode): ...@@ -4324,17 +4325,23 @@ class GeneralCallNode(CallNode):
if self.analyse_as_type_constructor(env): if self.analyse_as_type_constructor(env):
return return
self.function.analyse_types(env) self.function.analyse_types(env)
self.positional_args.analyse_types(env)
if self.keyword_args:
self.keyword_args.analyse_types(env)
if not self.function.type.is_pyobject: if not self.function.type.is_pyobject:
if self.function.type.is_error: if self.function.type.is_error:
self.type = error_type self.type = error_type
return return
if hasattr(self.function, 'entry') and not self.function.entry.as_variable: if hasattr(self.function, 'entry'):
error(self.pos, "Keyword and starred arguments not allowed in cdef functions.") self.map_keywords_to_posargs()
if not self.is_simple_call:
if self.function.entry.as_variable:
self.function = self.function.coerce_to_pyobject(env)
else:
error(self.pos, "Keyword and starred arguments "
"not allowed in cdef functions.")
else: else:
self.function = self.function.coerce_to_pyobject(env) self.function = self.function.coerce_to_pyobject(env)
if self.keyword_args:
self.keyword_args.analyse_types(env)
self.positional_args.analyse_types(env)
self.positional_args = \ self.positional_args = \
self.positional_args.coerce_to_pyobject(env) self.positional_args.coerce_to_pyobject(env)
function = self.function function = self.function
...@@ -4348,6 +4355,54 @@ class GeneralCallNode(CallNode): ...@@ -4348,6 +4355,54 @@ class GeneralCallNode(CallNode):
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
def map_keywords_to_posargs(self):
if not isinstance(self.positional_args, TupleNode):
# has starred argument
return
if not isinstance(self.keyword_args, DictNode):
# nothing to do here
return
function = self.function
entry = getattr(function, 'entry', None)
if not entry or not entry.is_cfunction:
return
args = self.positional_args.args
kwargs = self.keyword_args
declared_args = entry.type.args
if len(declared_args) < len(args):
# will lead to an error elsewhere
error(self.pos, "function call got too many positional arguments, "
"expected %d, got %s" % (len(declared_args), len(args)))
return
matched_pos_args = set([arg.name for arg in declared_args[:len(args)]])
unmatched_args = declared_args[len(args):]
matched_kwargs = set()
args = list(args)
# TODO: match keywords out-of-order and move values
# into ordered temps if necessary
for decl_arg, arg in zip(unmatched_args, kwargs.key_value_pairs):
name = arg.key.value
if name in matched_pos_args:
error(arg.pos, "keyword argument '%s' passed twice" % name)
return
if decl_arg.name == name:
matched_kwargs.add(name)
args.append(arg.value)
else:
break
if not matched_kwargs:
return
self.positional_args.args = args
if len(kwargs.key_value_pairs) == len(matched_kwargs):
self.keyword_args = None
self.is_simple_call = True
else:
kwargs.key_value_pairs = [
item for item in kwargs.key_value_pairs
if item.key.value not in matched_kwargs ]
def generate_result_code(self, code): def generate_result_code(self, code):
if self.type.is_error: return if self.type.is_error: return
if self.keyword_args: if self.keyword_args:
......
...@@ -1175,59 +1175,22 @@ class DropRefcountingTransform(Visitor.VisitorTransform): ...@@ -1175,59 +1175,22 @@ class DropRefcountingTransform(Visitor.VisitorTransform):
class SimplifyCalls(Visitor.EnvTransform): class SimplifyCalls(Visitor.EnvTransform):
""" """
Statically map keyword arguments in C calls to positional arguments. Replace GeneralCallNode by SimpleCallNode if possible.
""" """
def visit_GeneralCallNode(self, node): def visit_GeneralCallNode(self, node):
self.visitchildren(node) self.visitchildren(node)
arg_tuple = node.positional_args if not node.is_simple_call:
if not isinstance(arg_tuple, ExprNodes.TupleNode):
return node
args = arg_tuple.args
kwargs = node.keyword_args
function = node.function
if not function.is_name:
# TODO: optimise methods as well
return node return node
entry = function.entry args = [ unwrap_coerced_node(arg)
if not entry: for arg in node.positional_args.args ]
entry = self.current_env().lookup(function.name) call_node = ExprNodes.SimpleCallNode(
if not entry: node.pos,
return node function=node.function,
if not entry.is_cfunction or not entry.type: args=args)
return node call_node.analyse_types(self.current_env())
if node.type != call_node.type:
declared_args = entry.type.args call_node = call_node.coerce_to(node.type)
if len(declared_args) < len(args): return call_node
# will lead to an error elsewhere
return node
matched_pos_args = set([arg.name for arg in declared_args[:len(args)]])
unmatched_args = declared_args[len(args):]
matched_kwargs = set()
args = list(args)
# TODO: match keywords out-of-order and move values
# into ordered temps if necessary
for decl_arg, arg in zip(unmatched_args, kwargs.key_value_pairs):
name = arg.key.value
if name in matched_pos_args:
# keyword argument passed twice => should fail elsewhere
return node
if decl_arg.name == name:
matched_kwargs.add(name)
args.append(arg.value)
else:
break
if not matched_kwargs:
return node
if len(kwargs.key_value_pairs) == len(matched_kwargs):
return ExprNodes.SimpleCallNode(
node.pos,
function=function, args=args)
arg_tuple.args = args
kwargs.key_value_pairs = [
item for item in kwargs.key_value_pairs
if item.key.value not in matched_kwargs ]
return node
class EarlyReplaceBuiltinCalls(Visitor.EnvTransform): class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
......
...@@ -182,7 +182,6 @@ def create_pipeline(context, mode, exclude_classes=()): ...@@ -182,7 +182,6 @@ def create_pipeline(context, mode, exclude_classes=()):
AnalyseDeclarationsTransform(context), AnalyseDeclarationsTransform(context),
AutoTestDictTransform(context), AutoTestDictTransform(context),
EmbedSignature(context), EmbedSignature(context),
SimplifyCalls(context),
EarlyReplaceBuiltinCalls(context), ## Necessary? EarlyReplaceBuiltinCalls(context), ## Necessary?
TransformBuiltinMethods(context), ## Necessary? TransformBuiltinMethods(context), ## Necessary?
MarkParallelAssignments(context), MarkParallelAssignments(context),
...@@ -194,6 +193,7 @@ def create_pipeline(context, mode, exclude_classes=()): ...@@ -194,6 +193,7 @@ def create_pipeline(context, mode, exclude_classes=()):
_check_c_declarations, _check_c_declarations,
InlineDefNodeCalls(context), InlineDefNodeCalls(context),
AnalyseExpressionsTransform(context), AnalyseExpressionsTransform(context),
SimplifyCalls(context),
FindInvalidUseOfFusedTypes(context), FindInvalidUseOfFusedTypes(context),
CreateClosureClasses(context), ## After all lookups and type inference CreateClosureClasses(context), ## After all lookups and type inference
ExpandInplaceOperators(context), ExpandInplaceOperators(context),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment