@@ -49,6 +49,31 @@ class SetItem(ir.Statement):
4949 index : ir .SSAValue = info .argument (print = False )
5050
5151
52+ @statement (dialect = dialect )
53+ class SetAttribute (ir .Statement ):
54+ name = "setattr"
55+ traits = frozenset ({lowering .FromPythonCall ()})
56+ obj : ir .SSAValue = info .argument (print = False )
57+ attr : str = info .attribute ()
58+ value : ir .SSAValue = info .argument (print = False )
59+
60+
61+ @statement (dialect = dialect )
62+ class TypeAssert (ir .Statement ):
63+ traits = frozenset ({lowering .FromPythonCall ()})
64+ got : ir .SSAValue = info .argument (print = False )
65+ expected : types .TypeAttribute = info .attribute ()
66+ result : ir .ResultValue = info .result ()
67+
68+ def __init__ (self , got : ir .SSAValue , * , expected : types .TypeAttribute ):
69+ super ().__init__ (
70+ args = (got ,),
71+ attributes = {"expected" : expected },
72+ result_types = (expected ,),
73+ args_slice = {"got" : 0 },
74+ )
75+
76+
5277@dialect .register
5378class Concrete (interp .MethodTable ):
5479
@@ -59,7 +84,37 @@ def alias(self, interp, frame: interp.Frame, stmt: Alias):
5984 @interp .impl (SetItem )
6085 def setindex (self , interp , frame : interp .Frame , stmt : SetItem ):
6186 frame .get (stmt .obj )[frame .get (stmt .index )] = frame .get (stmt .value )
62- return (None ,)
87+
88+ @interp .impl (SetAttribute )
89+ def set_attribute (self , interp , frame : interp .Frame , stmt : SetAttribute ):
90+ obj = frame .get (stmt .obj )
91+ value = frame .get (stmt .value )
92+ setattr (obj , stmt .attr , value )
93+
94+ # NOTE: we don't do much runtime type checking here, object with generic
95+ # types will unlikely work here.
96+ # TODO: consider runtime type checking by boxing the value
97+ @interp .impl (TypeAssert )
98+ def type_assert (self , interp_ , frame : interp .Frame , stmt : TypeAssert ):
99+ got = frame .get (stmt .got )
100+ got_type = types .PyClass (type (got ))
101+ if not got_type .is_subseteq (stmt .expected ):
102+ raise interp .WrapException (
103+ TypeError (f"Expected { stmt .expected } , got { got_type } " )
104+ )
105+ return (frame .get (stmt .got ),)
106+
107+
108+ @dialect .register (key = "typeinfer" )
109+ class TypeInfer (interp .MethodTable ):
110+ @interp .impl (TypeAssert )
111+ def type_assert (
112+ self , interp_ , frame : interp .Frame [types .TypeAttribute ], stmt : TypeAssert
113+ ):
114+ got = frame .get (stmt .got )
115+ if got .is_subseteq (stmt .expected ):
116+ return (got .meet (stmt .expected ),)
117+ return (types .Bottom ,)
63118
64119
65120@dialect .register
@@ -79,16 +134,43 @@ def lower_Assign(self, state: lowering.State, node: ast.Assign) -> lowering.Resu
79134 current_frame .defs [lhs_name ] = current_frame .push (stmt ).result
80135 case _:
81136 for target , value in zip (node .targets , result .data ):
82- match target :
83- # NOTE: if the name exists new ssa value will be
84- # used in the future to shadow the old one
85- case ast .Name (name , ast .Store ()):
86- value .name = name
87- current_frame .defs [name ] = value
88- case ast .Subscript (obj , slice ):
89- obj = state .lower (obj ).expect_one ()
90- slice = state .lower (slice ).expect_one ()
91- stmt = SetItem (obj = obj , index = slice , value = value )
92- current_frame .push (stmt )
93- case _:
94- raise lowering .BuildError (f"unsupported target { target } " )
137+ self .assign_item (state , target , value )
138+
139+ def lower_AnnAssign (
140+ self , state : lowering .State , node : ast .AnnAssign
141+ ) -> lowering .Result :
142+ type_hint = self .get_hint (state , node .annotation )
143+ value = state .lower (node .value ).expect_one ()
144+ stmt = state .current_frame .push (TypeAssert (got = value , expected = type_hint ))
145+ self .assign_item (state , node .target , stmt .result )
146+
147+ def lower_AugAssign (
148+ self , state : lowering .State , node : ast .AugAssign
149+ ) -> lowering .Result :
150+ self .assign_item (state , node .target , state .lower (node .value ).expect_one ())
151+
152+ @staticmethod
153+ def assign_item (state : lowering .State , target , value : ir .SSAValue ):
154+ current_frame = state .current_frame
155+ match target :
156+ case ast .Name (name , ast .Store ()):
157+ value .name = name
158+ current_frame .defs [name ] = value
159+ case ast .Attribute (obj , attr , ast .Store ()):
160+ obj = state .lower (obj ).expect_one ()
161+ stmt = SetAttribute (obj , value , attr = attr )
162+ current_frame .push (stmt )
163+ case ast .Subscript (obj , slice , ast .Store ()):
164+ obj = state .lower (obj ).expect_one ()
165+ slice = state .lower (slice ).expect_one ()
166+ stmt = SetItem (obj = obj , index = slice , value = value )
167+ current_frame .push (stmt )
168+ case _:
169+ raise lowering .BuildError (f"unsupported target { target } " )
170+
171+ @staticmethod
172+ def assert_assign_value_type (value : ir .SSAValue , type_hint : types .TypeAttribute ):
173+ value_type = value .type .meet (type_hint )
174+ if value_type is value_type .bottom ():
175+ raise lowering .BuildError (f"Cannot assign { value .type } to { type_hint } " )
176+ return value_type
0 commit comments