Skip to content

Commit

Permalink
Add typechecking for most builtin.
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Jun 26, 2015
1 parent 7520311 commit 7cd6011
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 22 deletions.
16 changes: 14 additions & 2 deletions artiq/py2llvm/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ def __init__(self, elt=None):
elt = types.TVar()
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():
return types.TBuiltin("function len")

Expand Down Expand Up @@ -80,7 +92,7 @@ def is_collection(typ):
return isinstance(typ, types.TTuple) or \
types.is_mono(typ, "list")

def is_function(typ, name):
def is_builtin(typ, name):
typ = typ.find()
return isinstance(typ, types.TBuiltin) and \
typ.name == "function " + name
typ.name == name
7 changes: 4 additions & 3 deletions artiq/py2llvm/prelude.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

def globals():
return {
"bool": builtins.TBool(),
"int": builtins.TInt(),
"float": builtins.TFloat(),
"bool": builtins.fn_bool(),
"int": builtins.fn_int(),
"float": builtins.fn_float(),
"list": builtins.fn_list(),
"len": builtins.fn_len(),
"round": builtins.fn_round(),
"range": builtins.fn_range(),
Expand Down
107 changes: 90 additions & 17 deletions artiq/py2llvm/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,62 +716,135 @@ def visit_comprehension(self, node):
self._unify_collection(element=node.target, collection=node.iter)

def visit_builtin_call(self, node):
if types.is_mono(node.func.type):
func_name = "function " + node.func.type.find().name
elif builtins.is_function(node.func.type):
func_name = node.func.type.find().name
typ = node.func.type.find()

def valid_form(signature):
return diagnostic.Diagnostic("note",
"{func} can be invoked as: {signature}",
{"func": func_name},
{"func": typ.name, "signature": signature},
node.func.loc)

def diagnose(valid_forms):
diag = diagnostic.Diagnostic("error",
"{func} cannot be invoked with these arguments",
{"func": func_name},
{"func": typ.name},
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_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_form("int() -> int(width='a)"),
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_form("float() -> float"),
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_form("list() -> list(elt='a)"),
# TODO: add this form when adding iterators
# 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_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_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
# elif builtins.is_function(node.type, "range"):
# elif builtins.is_builtin(typ, "function range"):
# valid_forms = lambda: [
# 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, step:'a) -> range(elt='a)"),
# ]
# 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: [
# ]

Expand All @@ -790,7 +863,7 @@ def visit_CallT(self, node):
if types.is_var(node.func.type):
return # not enough info yet
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):
diag = diagnostic.Diagnostic("error",
"cannot call this expression of type {type}",
Expand Down
30 changes: 30 additions & 0 deletions lit-test/py2llvm/typing/builtin_calls.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,32 @@
# RUN: %python -m artiq.py2llvm.typing %s >%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)

0 comments on commit 7cd6011

Please sign in to comment.