diff --git a/artiq/py2llvm/builtins.py b/artiq/py2llvm/builtins.py index c547ef374..84229cc6c 100644 --- a/artiq/py2llvm/builtins.py +++ b/artiq/py2llvm/builtins.py @@ -26,6 +26,8 @@ class TFloat(types.TMono): class TTuple(types.Type): """A tuple type.""" + attributes = {} + def __init__(self, elts=[]): self.elts = elts diff --git a/artiq/py2llvm/types.py b/artiq/py2llvm/types.py index 2397ea61f..cce235ed8 100644 --- a/artiq/py2llvm/types.py +++ b/artiq/py2llvm/types.py @@ -37,8 +37,6 @@ class TVar(Type): folded into this class. """ - attributes = () - def __init__(self): self.parent = self @@ -71,6 +69,8 @@ class TVar(Type): class TMono(Type): """A monomorphic type, possibly parametric.""" + attributes = {} + def __init__(self, name, params={}): self.name, self.params = name, params diff --git a/artiq/py2llvm/typing.py b/artiq/py2llvm/typing.py index 33e8c4dc2..a35b763f6 100644 --- a/artiq/py2llvm/typing.py +++ b/artiq/py2llvm/typing.py @@ -224,6 +224,13 @@ class ASTTypedRewriter(algorithm.Transformer): elts=node.elts, ctx=node.ctx, loc=node.loc) return self.visit(node) + def visit_Attribute(self, node): + node = self.generic_visit(node) + node = asttyped.AttributeT(type=types.TVar(), + value=node.value, attr=node.attr, ctx=node.ctx, + dot_loc=node.dot_loc, attr_loc=node.attr_loc, loc=node.loc) + return self.visit(node) + def visit_Subscript(self, node): node = self.generic_visit(node) node = asttyped.SubscriptT(type=types.TVar(), @@ -231,13 +238,6 @@ class ASTTypedRewriter(algorithm.Transformer): loc=node.loc) return self.visit(node) - def visit_IfExp(self, node): - node = self.generic_visit(node) - node = asttyped.IfExpT(type=types.TVar(), - test=node.test, body=node.body, orelse=node.orelse, - if_loc=node.if_loc, else_loc=node.else_loc, loc=node.loc) - return self.visit(node) - def visit_BoolOp(self, node): node = self.generic_visit(node) node = asttyped.BoolOpT(type=types.TVar(), @@ -266,6 +266,13 @@ class ASTTypedRewriter(algorithm.Transformer): loc=node.loc) return self.visit(node) + def visit_IfExp(self, node): + node = self.generic_visit(node) + node = asttyped.IfExpT(type=types.TVar(), + test=node.test, body=node.body, orelse=node.orelse, + if_loc=node.if_loc, else_loc=node.else_loc, loc=node.loc) + return self.visit(node) + # Unsupported visitors # def visit_unsupported(self, node): @@ -275,7 +282,6 @@ class ASTTypedRewriter(algorithm.Transformer): self.engine.process(diag) # expr - visit_Attribute = visit_unsupported visit_BinOp = visit_unsupported visit_Call = visit_unsupported visit_Compare = visit_unsupported @@ -373,6 +379,19 @@ class Inferencer(algorithm.Visitor): self._unify(node.type["elt"], elt.type, node.loc, elt.loc, self._makenotes_elts(node.elts, "a list element")) + def visit_AttributeT(self, node): + object_type = node.value.type.find() + if not types.is_var(object_type): + if node.attr in object_type.attributes: + # assumes no free type variables in .attributes + node.type = object_type.attributes[node.attr] + else: + diag = diagnostic.Diagnostic("error", + "type {type} does not have an attribute '{attr}'", + {"type": types.TypePrinter().name(object_type), "attr": node.attr}, + node.attr_loc, [node.value.loc]) + self.engine.process(diag) + def visit_SubscriptT(self, node): # TODO: support more than just lists self._unify(builtins.TList(node.type), node.value.type, diff --git a/lit-test/py2llvm/typing/error_unify.py b/lit-test/py2llvm/typing/error_unify.py index 73641b8c6..d268837e1 100644 --- a/lit-test/py2llvm/typing/error_unify.py +++ b/lit-test/py2llvm/typing/error_unify.py @@ -22,3 +22,6 @@ a = b # CHECK-L: ${LINE:+1}: error: expected ~ operand to be of integer type, not float ~1.0 + +# CHECK-L: ${LINE:+1}: error: type int(width='a) does not have an attribute 'x' +(1).x