forked from M-Labs/artiq
Add typechecking for most builtin.
This commit is contained in:
parent
752031147d
commit
7cd6011981
|
@ -31,6 +31,18 @@ class TList(types.TMono):
|
||||||
elt = types.TVar()
|
elt = types.TVar()
|
||||||
super().__init__("list", {"elt": elt})
|
super().__init__("list", {"elt": elt})
|
||||||
|
|
||||||
|
def fn_bool():
|
||||||
|
return types.TBuiltin("class bool")
|
||||||
|
|
||||||
|
def fn_int():
|
||||||
|
return types.TBuiltin("class int")
|
||||||
|
|
||||||
|
def fn_float():
|
||||||
|
return types.TBuiltin("class float")
|
||||||
|
|
||||||
|
def fn_list():
|
||||||
|
return types.TBuiltin("class list")
|
||||||
|
|
||||||
def fn_len():
|
def fn_len():
|
||||||
return types.TBuiltin("function len")
|
return types.TBuiltin("function len")
|
||||||
|
|
||||||
|
@ -80,7 +92,7 @@ def is_collection(typ):
|
||||||
return isinstance(typ, types.TTuple) or \
|
return isinstance(typ, types.TTuple) or \
|
||||||
types.is_mono(typ, "list")
|
types.is_mono(typ, "list")
|
||||||
|
|
||||||
def is_function(typ, name):
|
def is_builtin(typ, name):
|
||||||
typ = typ.find()
|
typ = typ.find()
|
||||||
return isinstance(typ, types.TBuiltin) and \
|
return isinstance(typ, types.TBuiltin) and \
|
||||||
typ.name == "function " + name
|
typ.name == name
|
||||||
|
|
|
@ -7,9 +7,10 @@ from . import builtins
|
||||||
|
|
||||||
def globals():
|
def globals():
|
||||||
return {
|
return {
|
||||||
"bool": builtins.TBool(),
|
"bool": builtins.fn_bool(),
|
||||||
"int": builtins.TInt(),
|
"int": builtins.fn_int(),
|
||||||
"float": builtins.TFloat(),
|
"float": builtins.fn_float(),
|
||||||
|
"list": builtins.fn_list(),
|
||||||
"len": builtins.fn_len(),
|
"len": builtins.fn_len(),
|
||||||
"round": builtins.fn_round(),
|
"round": builtins.fn_round(),
|
||||||
"range": builtins.fn_range(),
|
"range": builtins.fn_range(),
|
||||||
|
|
|
@ -716,62 +716,135 @@ class Inferencer(algorithm.Visitor):
|
||||||
self._unify_collection(element=node.target, collection=node.iter)
|
self._unify_collection(element=node.target, collection=node.iter)
|
||||||
|
|
||||||
def visit_builtin_call(self, node):
|
def visit_builtin_call(self, node):
|
||||||
if types.is_mono(node.func.type):
|
typ = node.func.type.find()
|
||||||
func_name = "function " + node.func.type.find().name
|
|
||||||
elif builtins.is_function(node.func.type):
|
|
||||||
func_name = node.func.type.find().name
|
|
||||||
|
|
||||||
def valid_form(signature):
|
def valid_form(signature):
|
||||||
return diagnostic.Diagnostic("note",
|
return diagnostic.Diagnostic("note",
|
||||||
"{func} can be invoked as: {signature}",
|
"{func} can be invoked as: {signature}",
|
||||||
{"func": func_name},
|
{"func": typ.name, "signature": signature},
|
||||||
node.func.loc)
|
node.func.loc)
|
||||||
|
|
||||||
def diagnose(valid_forms):
|
def diagnose(valid_forms):
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"{func} cannot be invoked with these arguments",
|
"{func} cannot be invoked with these arguments",
|
||||||
{"func": func_name},
|
{"func": typ.name},
|
||||||
node.func.loc, notes=valid_forms)
|
node.func.loc, notes=valid_forms)
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
if builtins.is_bool(node.type):
|
if builtins.is_builtin(typ, "class bool"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
valid_form("bool() -> bool"),
|
valid_form("bool() -> bool"),
|
||||||
valid_form("bool(x:'a) -> bool where 'a is numeric")
|
valid_form("bool(x:'a) -> bool")
|
||||||
]
|
]
|
||||||
elif builtins.is_int(node.type):
|
|
||||||
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||||
|
pass # False
|
||||||
|
elif len(node.args) == 1 and len(node.keywords) == 0:
|
||||||
|
arg, = node.args
|
||||||
|
pass # anything goes
|
||||||
|
else:
|
||||||
|
diagnose(valid_forms())
|
||||||
|
|
||||||
|
self._unify(node.type, builtins.TBool(),
|
||||||
|
node.loc, None)
|
||||||
|
elif builtins.is_builtin(typ, "class int"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
valid_form("int() -> int(width='a)"),
|
valid_form("int() -> int(width='a)"),
|
||||||
valid_form("int(x:'a) -> int(width='b) where 'a is numeric"),
|
valid_form("int(x:'a) -> int(width='b) where 'a is numeric"),
|
||||||
valid_form("int(x:'a, width='b <int literal>) -> int(width='b) where 'a is numeric")
|
valid_form("int(x:'a, width='b:<int literal>) -> int(width='b) where 'a is numeric")
|
||||||
]
|
]
|
||||||
elif builtins.is_float(node.type):
|
|
||||||
|
self._unify(node.type, builtins.TInt(),
|
||||||
|
node.loc, None)
|
||||||
|
|
||||||
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||||
|
pass # 0
|
||||||
|
elif len(node.args) == 1 and len(node.keywords) == 0 and \
|
||||||
|
builtins.is_numeric(node.args[0].type):
|
||||||
|
pass
|
||||||
|
elif len(node.args) == 1 and len(node.keywords) == 1 and \
|
||||||
|
builtins.is_numeric(node.args[0].type) and \
|
||||||
|
node.keywords[0].arg == 'width':
|
||||||
|
width = node.keywords[0].value
|
||||||
|
if not (isinstance(width, asttyped.NumT) and isinstance(width.n, int)):
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"the width argument of int() must be an integer literal", {},
|
||||||
|
node.keywords[0].loc)
|
||||||
|
|
||||||
|
self._unify(node.type, builtins.TInt(types.TValue(width.n)),
|
||||||
|
node.loc, None)
|
||||||
|
else:
|
||||||
|
diagnose(valid_forms())
|
||||||
|
elif builtins.is_builtin(typ, "class float"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
valid_form("float() -> float"),
|
valid_form("float() -> float"),
|
||||||
valid_form("float(x:'a) -> float where 'a is numeric")
|
valid_form("float(x:'a) -> float where 'a is numeric")
|
||||||
]
|
]
|
||||||
elif builtins.is_list(node.type):
|
|
||||||
|
self._unify(node.type, builtins.TFloat(),
|
||||||
|
node.loc, None)
|
||||||
|
|
||||||
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||||
|
pass # 0.0
|
||||||
|
elif len(node.args) == 1 and len(node.keywords) == 0 and \
|
||||||
|
builtins.is_numeric(node.args[0].type):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
diagnose(valid_forms())
|
||||||
|
elif builtins.is_builtin(typ, "class list"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
valid_form("list() -> list(elt='a)"),
|
valid_form("list() -> list(elt='a)"),
|
||||||
# TODO: add this form when adding iterators
|
# TODO: add this form when adding iterators
|
||||||
# valid_form("list(x) -> list(elt='a)")
|
# valid_form("list(x) -> list(elt='a)")
|
||||||
]
|
]
|
||||||
elif builtins.is_function(node.type, "len"):
|
|
||||||
|
self._unify(node.type, builtins.TList(),
|
||||||
|
node.loc, None)
|
||||||
|
|
||||||
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||||
|
pass # []
|
||||||
|
else:
|
||||||
|
diagnose(valid_forms())
|
||||||
|
elif builtins.is_builtin(typ, "function len"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
valid_form("len(x:list(elt='a)) -> int(width='b)"),
|
valid_form("len(x:list(elt='a)) -> int(width='b)"),
|
||||||
]
|
]
|
||||||
elif builtins.is_function(node.type, "round"):
|
|
||||||
|
# TODO: should be ssize_t-sized
|
||||||
|
self._unify(node.type, builtins.TInt(types.TValue(32)),
|
||||||
|
node.loc, None)
|
||||||
|
|
||||||
|
if len(node.args) == 1 and len(node.keywords) == 0:
|
||||||
|
arg, = node.args
|
||||||
|
|
||||||
|
self._unify(arg.type, builtins.TList(),
|
||||||
|
arg.loc, None)
|
||||||
|
else:
|
||||||
|
diagnose(valid_forms())
|
||||||
|
elif builtins.is_builtin(typ, "function round"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
valid_form("round(x:float) -> int(width='a)"),
|
valid_form("round(x:float) -> int(width='a)"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
self._unify(node.type, builtins.TInt(),
|
||||||
|
node.loc, None)
|
||||||
|
|
||||||
|
if len(node.args) == 1 and len(node.keywords) == 0:
|
||||||
|
arg, = node.args
|
||||||
|
|
||||||
|
self._unify(arg.type, builtins.TFloat(),
|
||||||
|
arg.loc, None)
|
||||||
|
else:
|
||||||
|
diagnose(valid_forms())
|
||||||
# TODO: add when there are range types
|
# TODO: add when there are range types
|
||||||
# elif builtins.is_function(node.type, "range"):
|
# elif builtins.is_builtin(typ, "function range"):
|
||||||
# valid_forms = lambda: [
|
# valid_forms = lambda: [
|
||||||
# valid_form("range(max:'a) -> range(elt='a)"),
|
# valid_form("range(max:'a) -> range(elt='a)"),
|
||||||
# valid_form("range(min:'a, max:'a) -> range(elt='a)"),
|
# valid_form("range(min:'a, max:'a) -> range(elt='a)"),
|
||||||
# valid_form("range(min:'a, max:'a, step:'a) -> range(elt='a)"),
|
# valid_form("range(min:'a, max:'a, step:'a) -> range(elt='a)"),
|
||||||
# ]
|
# ]
|
||||||
# TODO: add when it is clear what interface syscall() has
|
# TODO: add when it is clear what interface syscall() has
|
||||||
# elif builtins.is_function(node.type, "syscall"):
|
# elif builtins.is_builtin(typ, "function syscall"):
|
||||||
# valid_Forms = lambda: [
|
# valid_Forms = lambda: [
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
|
@ -790,7 +863,7 @@ class Inferencer(algorithm.Visitor):
|
||||||
if types.is_var(node.func.type):
|
if types.is_var(node.func.type):
|
||||||
return # not enough info yet
|
return # not enough info yet
|
||||||
elif types.is_mono(node.func.type) or types.is_builtin(node.func.type):
|
elif types.is_mono(node.func.type) or types.is_builtin(node.func.type):
|
||||||
return self.visit_builtin_call(self, node)
|
return self.visit_builtin_call(node)
|
||||||
elif not types.is_function(node.func.type):
|
elif not types.is_function(node.func.type):
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"cannot call this expression of type {type}",
|
"cannot call this expression of type {type}",
|
||||||
|
|
|
@ -1,2 +1,32 @@
|
||||||
# RUN: %python -m artiq.py2llvm.typing %s >%t
|
# RUN: %python -m artiq.py2llvm.typing %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: bool:<built-in class bool>():bool
|
||||||
|
bool()
|
||||||
|
|
||||||
|
# CHECK-L: bool:<built-in class bool>([]:list(elt='a)):bool
|
||||||
|
bool([])
|
||||||
|
|
||||||
|
# CHECK-L: int:<built-in class int>():int(width='b)
|
||||||
|
int()
|
||||||
|
|
||||||
|
# CHECK-L: int:<built-in class int>(1.0:float):int(width='c)
|
||||||
|
int(1.0)
|
||||||
|
|
||||||
|
# CHECK-L: int:<built-in class int>(1.0:float, width=64:int(width='d)):int(width=64)
|
||||||
|
int(1.0, width=64)
|
||||||
|
|
||||||
|
# CHECK-L: float:<built-in class float>():float
|
||||||
|
float()
|
||||||
|
|
||||||
|
# CHECK-L: float:<built-in class float>(1:int(width='e)):float
|
||||||
|
float(1)
|
||||||
|
|
||||||
|
# CHECK-L: list:<built-in class list>():list(elt='f)
|
||||||
|
list()
|
||||||
|
|
||||||
|
# CHECK-L: len:<built-in function len>([]:list(elt='g)):int(width=32)
|
||||||
|
len([])
|
||||||
|
|
||||||
|
# CHECK-L: round:<built-in function round>(1.0:float):int(width='h)
|
||||||
|
round(1.0)
|
||||||
|
|
Loading…
Reference in New Issue