@@ -124,16 +124,19 @@ def lift(value: typing.Any, /, type: types.Type | None = None) -> Expr:
124124 from qiskit .circuit import Clbit , ClassicalRegister # pylint: disable=cyclic-import
125125
126126 inferred : types .Type
127- if value is True or value is False or isinstance (value , Clbit ):
127+ if value is True or value is False :
128+ inferred = types .Bool (const = True )
129+ constructor = Value
130+ elif isinstance (value , Clbit ):
128131 inferred = types .Bool ()
129- constructor = Value if value is True or value is False else Var
132+ constructor = Var
130133 elif isinstance (value , ClassicalRegister ):
131134 inferred = types .Uint (width = value .size )
132135 constructor = Var
133136 elif isinstance (value , int ):
134137 if value < 0 :
135138 raise ValueError ("cannot represent a negative value" )
136- inferred = types .Uint (width = value .bit_length () or 1 )
139+ inferred = types .Uint (width = value .bit_length () or 1 , const = True )
137140 constructor = Value
138141 else :
139142 raise TypeError (f"failed to infer a type for '{ value } '" )
@@ -198,41 +201,73 @@ def logic_not(operand: typing.Any, /) -> Expr:
198201 Cast(Var(ClassicalRegister(3, 'c'), Uint(3)), Bool(), implicit=True), \
199202 Bool())
200203 """
201- operand = _coerce_lossless (lift (operand ), types .Bool ())
204+ var_or_value = lift (operand )
205+ operand = _coerce_lossless (var_or_value , types .Bool (const = var_or_value .type .const ))
202206 return Unary (Unary .Op .LOGIC_NOT , operand , operand .type )
203207
204208
205209def _lift_binary_operands (left : typing .Any , right : typing .Any ) -> tuple [Expr , Expr ]:
206210 """Lift two binary operands simultaneously, inferring the widths of integer literals in either
207- position to match the other operand."""
208- left_int = isinstance (left , int ) and not isinstance (left , bool )
209- right_int = isinstance (right , int ) and not isinstance (right , bool )
211+ position to match the other operand.
212+
213+ Const-ness is handled as follows:
214+ * If neither operand is an expression, both are lifted to share the same const-ness.
215+ Both will be const, if possible. Else, neither will be.
216+ * If only one operand is an expression, the other is lifted with the same const-ness, if possible.
217+ Otherwise, the returned operands will have different const-ness, and thus require a cast node.
218+ * If both operands are expressions, they are returned as-is and may require a cast node.
219+ """
220+ left_bool = isinstance (left , bool )
221+ left_int = isinstance (left , int ) and not left_bool
222+ right_bool = isinstance (right , bool )
223+ right_int = isinstance (right , int ) and not right_bool
210224 if not (left_int or right_int ):
211- left = lift (left )
212- right = lift (right )
225+ if left_bool == right_bool :
226+ # If they're both bool, lifting them will produce const Bool.
227+ # If neither are bool, they're a mix of bits/registers (which are always
228+ # non-const) and Expr, which we can't modify the const-ness of without
229+ # a cast node.
230+ left = lift (left )
231+ right = lift (right )
232+ elif not right_bool :
233+ # Left is a bool
234+ right = lift (right )
235+ # TODO: if right.type isn't Bool, there's a type mismatch so we _should_
236+ # raise here. But, _binary_bitwise will error for us with a better msg.
237+ left = lift (left , right .type if right .type .kind is types .Bool else None )
238+ elif not left_bool :
239+ # Right is a bool.
240+ left = lift (left )
241+ # TODO: if left.type isn't Bool, there's a type mismatch so we _should_
242+ # raise here. But, _binary_bitwise will error for us with a better msg.
243+ right = lift (right , left .type if left .type .kind is types .Bool else None )
213244 elif not right_int :
245+ # Left is an int.
214246 right = lift (right )
215247 if right .type .kind is types .Uint :
216248 if left .bit_length () > right .type .width :
217249 raise TypeError (
218250 f"integer literal '{ left } ' is wider than the other operand '{ right } '"
219251 )
252+ # Left will share const-ness of right.
220253 left = Value (left , right .type )
221254 else :
222255 left = lift (left )
223256 elif not left_int :
257+ # Right is an int.
224258 left = lift (left )
225259 if left .type .kind is types .Uint :
226260 if right .bit_length () > left .type .width :
227261 raise TypeError (
228262 f"integer literal '{ right } ' is wider than the other operand '{ left } '"
229263 )
264+ # Right will share const-ness of left.
230265 right = Value (right , left .type )
231266 else :
232267 right = lift (right )
233268 else :
234269 # Both are `int`, so we take our best case to make things work.
235- uint = types .Uint (max (left .bit_length (), right .bit_length (), 1 ))
270+ uint = types .Uint (max (left .bit_length (), right .bit_length (), 1 ), const = True )
236271 left = Value (left , uint )
237272 right = Value (right , uint )
238273 return left , right
@@ -242,14 +277,14 @@ def _binary_bitwise(op: Binary.Op, left: typing.Any, right: typing.Any) -> Expr:
242277 left , right = _lift_binary_operands (left , right )
243278 type : types .Type
244279 if left .type .kind is right .type .kind is types .Bool :
245- type = types .Bool ()
280+ type = types .Bool (const = ( left . type . const and right . type . const ) )
246281 elif left .type .kind is types .Uint and right .type .kind is types .Uint :
247282 if left .type != right .type :
248283 raise TypeError (
249284 "binary bitwise operations are defined between unsigned integers of the same width,"
250285 f" but got { left .type .width } and { right .type .width } ."
251286 )
252- type = left .type
287+ type = types . Uint ( width = left .type . width , const = ( left . type . const and right . type . const ))
253288 else :
254289 raise TypeError (f"invalid types for '{ op } ': '{ left .type } ' and '{ right .type } '" )
255290 return Binary (op , left , right , type )
@@ -313,10 +348,10 @@ def bit_xor(left: typing.Any, right: typing.Any, /) -> Expr:
313348
314349
315350def _binary_logical (op : Binary .Op , left : typing .Any , right : typing .Any ) -> Expr :
316- bool_ = types . Bool ( )
317- left = _coerce_lossless (lift ( left ), bool_ )
318- right = _coerce_lossless (lift ( right ), bool_ )
319- return Binary (op , left , right , bool_ )
351+ left , right = _lift_binary_operands ( left , right )
352+ left = _coerce_lossless (left , types . Bool ( const = left . type . const ) )
353+ right = _coerce_lossless (right , types . Bool ( const = right . type . const ) )
354+ return Binary (op , left , right , types . Bool ( const = ( left . type . const and right . type . const )) )
320355
321356
322357def logic_and (left : typing .Any , right : typing .Any , / ) -> Expr :
@@ -354,7 +389,7 @@ def _equal_like(op: Binary.Op, left: typing.Any, right: typing.Any) -> Expr:
354389 if left .type .kind is not right .type .kind :
355390 raise TypeError (f"invalid types for '{ op } ': '{ left .type } ' and '{ right .type } '" )
356391 type = types .greater (left .type , right .type )
357- return Binary (op , _coerce_lossless (left , type ), _coerce_lossless (right , type ), types .Bool ())
392+ return Binary (op , _coerce_lossless (left , type ), _coerce_lossless (right , type ), types .Bool (const = type . const ))
358393
359394
360395def equal (left : typing .Any , right : typing .Any , / ) -> Expr :
@@ -398,7 +433,7 @@ def _binary_relation(op: Binary.Op, left: typing.Any, right: typing.Any) -> Expr
398433 if left .type .kind is not right .type .kind or left .type .kind is types .Bool :
399434 raise TypeError (f"invalid types for '{ op } ': '{ left .type } ' and '{ right .type } '" )
400435 type = types .greater (left .type , right .type )
401- return Binary (op , _coerce_lossless (left , type ), _coerce_lossless (right , type ), types .Bool ())
436+ return Binary (op , _coerce_lossless (left , type ), _coerce_lossless (right , type ), types .Bool (const = type . const ))
402437
403438
404439def less (left : typing .Any , right : typing .Any , / ) -> Expr :
@@ -485,7 +520,7 @@ def _shift_like(
485520 right = lift (right )
486521 if left .type .kind != types .Uint or right .type .kind != types .Uint :
487522 raise TypeError (f"invalid types for '{ op } ': '{ left .type } ' and '{ right .type } '" )
488- return Binary (op , left , right , left .type )
523+ return Binary (op , left , right , types . Uint ( width = left .type . width , const = ( left . type . const and right . type . const )) )
489524
490525
491526def shift_left (left : typing .Any , right : typing .Any , / , type : types .Type | None = None ) -> Expr :
@@ -553,4 +588,4 @@ def index(target: typing.Any, index: typing.Any, /) -> Expr:
553588 target , index = lift (target ), lift (index )
554589 if target .type .kind is not types .Uint or index .type .kind is not types .Uint :
555590 raise TypeError (f"invalid types for indexing: '{ target .type } ' and '{ index .type } '" )
556- return Index (target , index , types .Bool ())
591+ return Index (target , index , types .Bool (const = target . type . const ))
0 commit comments