1use std::borrow::Cow;
21use std::cmp::Ordering;
22
23use dupe::Dupe;
24use starlark_derive::VisitSpanMut;
25use starlark_syntax::slice_vec_ext::SliceExt;
26use starlark_syntax::syntax::ast::AstExprP;
27use starlark_syntax::syntax::ast::AstLiteral;
28use starlark_syntax::syntax::ast::AstPayload;
29use starlark_syntax::syntax::ast::BinOp;
30use starlark_syntax::syntax::ast::ExprP;
31use starlark_syntax::syntax::ast::FStringP;
32use starlark_syntax::syntax::ast::LambdaP;
33use starlark_syntax::syntax::ast::StmtP;
34use thiserror::Error;
35
36use crate::codemap::Spanned;
37use crate::collections::symbol::symbol::Symbol;
38use crate::environment::slots::ModuleSlotId;
39use crate::errors::did_you_mean::did_you_mean;
40use crate::eval::compiler::args::ArgsCompiledValue;
41use crate::eval::compiler::call::CallCompiled;
42use crate::eval::compiler::compr::ComprCompiled;
43use crate::eval::compiler::constants::Constants;
44use crate::eval::compiler::def::DefCompiled;
45use crate::eval::compiler::def::FrozenDef;
46use crate::eval::compiler::error::CompilerInternalError;
47use crate::eval::compiler::expr_bool::ExprCompiledBool;
48use crate::eval::compiler::known::list_to_tuple;
49use crate::eval::compiler::opt_ctx::OptCtx;
50use crate::eval::compiler::scope::payload::CstExpr;
51use crate::eval::compiler::scope::payload::CstIdent;
52use crate::eval::compiler::scope::AssignCount;
53use crate::eval::compiler::scope::Captured;
54use crate::eval::compiler::scope::ResolvedIdent;
55use crate::eval::compiler::scope::Slot;
56use crate::eval::compiler::span::IrSpanned;
57use crate::eval::compiler::Compiler;
58use crate::eval::runtime::frame_span::FrameSpan;
59use crate::eval::runtime::frozen_file_span::FrozenFileSpan;
60use crate::eval::runtime::slots::LocalCapturedSlotId;
61use crate::eval::runtime::slots::LocalSlotId;
62use crate::eval::Arguments;
63use crate::eval::Evaluator;
64use crate::values::bool::StarlarkBool;
65use crate::values::function::BoundMethodGen;
66use crate::values::function::FrozenBoundMethod;
67use crate::values::list::ListRef;
68use crate::values::range::Range;
69use crate::values::string::interpolation::parse_percent_s_one;
70use crate::values::types::dict::Dict;
71use crate::values::types::ellipsis::Ellipsis;
72use crate::values::types::float::StarlarkFloat;
73use crate::values::types::int::inline_int::InlineInt;
74use crate::values::types::int::int_or_big::StarlarkInt;
75use crate::values::types::list::value::FrozenListData;
76use crate::values::types::list::value::ListData;
77use crate::values::types::string::dot_format::format_one;
78use crate::values::types::string::interpolation::percent_s_one;
79use crate::values::types::tuple::value::Tuple;
80use crate::values::types::unbound::UnboundValue;
81use crate::values::FrozenHeap;
82use crate::values::FrozenRef;
83use crate::values::FrozenStringValue;
84use crate::values::FrozenValue;
85use crate::values::FrozenValueTyped;
86use crate::values::Heap;
87use crate::values::StarlarkValue;
88use crate::values::Value;
89use crate::values::ValueError;
90use crate::values::ValueLike;
91
92#[derive(Copy, Clone, Dupe, Eq, PartialEq, Debug)]
94pub(crate) enum MaybeNot {
95 Id,
96 Not,
97}
98
99impl MaybeNot {
100 pub(crate) fn negate(self) -> MaybeNot {
101 match self {
102 MaybeNot::Id => MaybeNot::Not,
103 MaybeNot::Not => MaybeNot::Id,
104 }
105 }
106}
107
108#[derive(Copy, Clone, Dupe, Debug)]
110pub(crate) enum CompareOp {
111 Less,
112 Greater,
113 LessOrEqual,
114 GreaterOrEqual,
115}
116
117impl CompareOp {
118 fn apply(self, x: Ordering) -> bool {
119 match self {
120 CompareOp::Less => x == Ordering::Less,
121 CompareOp::Greater => x == Ordering::Greater,
122 CompareOp::LessOrEqual => x != Ordering::Greater,
123 CompareOp::GreaterOrEqual => x != Ordering::Less,
124 }
125 }
126}
127
128#[derive(Clone, Debug, VisitSpanMut)]
130pub(crate) enum Builtin1 {
131 Minus,
132 Plus,
134 BitNot,
136 Not,
138 TypeIs(FrozenStringValue),
140 PercentSOne(FrozenStringValue, FrozenStringValue),
142 FormatOne(FrozenStringValue, FrozenStringValue),
144 Dot(Symbol),
146}
147
148impl Builtin1 {
149 fn eval<'v>(&self, v: FrozenValue, ctx: &mut OptCtx<'v, '_, '_, '_>) -> Option<Value<'v>> {
150 match self {
151 Builtin1::Minus => v.to_value().minus(ctx.heap()).ok(),
152 Builtin1::Plus => v.to_value().plus(ctx.heap()).ok(),
153 Builtin1::BitNot => v.to_value().bit_not(ctx.heap()).ok(),
154 Builtin1::Not => Some(Value::new_bool(!v.to_value().to_bool())),
155 Builtin1::TypeIs(t) => Some(Value::new_bool(v.to_value().get_type_value() == *t)),
156 Builtin1::FormatOne(before, after) => {
157 Some(format_one(before, v.to_value(), after, ctx.heap()).to_value())
158 }
159 Builtin1::PercentSOne(before, after) => {
160 percent_s_one(before, v.to_value(), after, ctx.heap())
161 .map(|s| s.to_value())
162 .ok()
163 }
164 Builtin1::Dot(field) => {
165 Some(ExprCompiled::compile_time_getattr(v, field, ctx)?.to_value())
166 }
167 }
168 }
169}
170
171#[derive(Copy, Clone, Dupe, Debug, VisitSpanMut)]
173pub(crate) enum Builtin2 {
174 Equals,
176 In,
178 Sub,
180 Add,
182 Multiply,
184 Percent,
186 Divide,
188 FloorDivide,
190 BitAnd,
192 BitOr,
194 BitXor,
196 LeftShift,
198 RightShift,
200 Compare(CompareOp),
202 ArrayIndex,
204}
205
206impl Builtin2 {
207 fn eval<'v>(self, a: Value<'v>, b: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
208 match self {
209 Builtin2::Equals => a.equals(b).map(Value::new_bool),
210 Builtin2::Compare(cmp) => a.compare(b).map(|c| Value::new_bool(cmp.apply(c))),
211 Builtin2::In => b.is_in(a).map(Value::new_bool),
212 Builtin2::Sub => a.sub(b, heap),
213 Builtin2::Add => a.add(b, heap),
214 Builtin2::Multiply => a.mul(b, heap),
215 Builtin2::Percent => a.percent(b, heap),
216 Builtin2::Divide => a.div(b, heap),
217 Builtin2::FloorDivide => a.floor_div(b, heap),
218 Builtin2::BitAnd => a.bit_and(b, heap),
219 Builtin2::BitOr => a.bit_or(b, heap),
220 Builtin2::BitXor => a.bit_xor(b, heap),
221 Builtin2::LeftShift => a.left_shift(b, heap),
222 Builtin2::RightShift => a.right_shift(b, heap),
223 Builtin2::ArrayIndex => a.at(b, heap),
224 }
225 }
226}
227
228#[derive(Copy, Clone, Dupe, Debug, VisitSpanMut, Eq, PartialEq)]
230pub(crate) enum ExprLogicalBinOp {
231 And,
232 Or,
233}
234
235#[derive(Clone, Debug, VisitSpanMut)]
236pub(crate) enum ExprCompiled {
237 Value(FrozenValue),
238 Local(LocalSlotId),
240 LocalCaptured(LocalCapturedSlotId),
242 Module(ModuleSlotId),
243 Tuple(Vec<IrSpanned<ExprCompiled>>),
244 List(Vec<IrSpanned<ExprCompiled>>),
245 Dict(Vec<(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)>),
246 Compr(ComprCompiled),
248 If(
249 Box<(
250 IrSpanned<ExprCompiled>,
252 IrSpanned<ExprCompiled>,
254 IrSpanned<ExprCompiled>,
256 )>,
257 ),
258 Slice(
259 Box<(
260 IrSpanned<ExprCompiled>,
261 Option<IrSpanned<ExprCompiled>>,
262 Option<IrSpanned<ExprCompiled>>,
263 Option<IrSpanned<ExprCompiled>>,
264 )>,
265 ),
266 Builtin1(Builtin1, Box<IrSpanned<ExprCompiled>>),
267 LogicalBinOp(
268 ExprLogicalBinOp,
269 Box<(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)>,
270 ),
271 Seq(Box<(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)>),
274 Builtin2(
275 Builtin2,
276 Box<(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)>,
277 ),
278 Index2(
279 Box<(
280 IrSpanned<ExprCompiled>,
281 IrSpanned<ExprCompiled>,
282 IrSpanned<ExprCompiled>,
283 )>,
284 ),
285 Call(Box<IrSpanned<CallCompiled>>),
286 Def(DefCompiled),
287}
288
289impl ExprCompiled {
290 pub fn as_value(&self) -> Option<FrozenValue> {
291 match self {
292 Self::Value(x) => Some(*x),
293 _ => None,
294 }
295 }
296
297 pub(crate) fn as_frozen_def(&self) -> Option<FrozenValueTyped<FrozenDef>> {
299 FrozenValueTyped::new(self.as_value()?)
300 }
301
302 pub(crate) fn as_frozen_bound_method(&self) -> Option<FrozenValueTyped<FrozenBoundMethod>> {
304 FrozenValueTyped::new(self.as_value()?)
305 }
306
307 pub(crate) fn is_fn_len(&self) -> bool {
309 match self.as_value() {
310 Some(value) => value == Constants::get().fn_len,
311 None => false,
312 }
313 }
314
315 pub(crate) fn is_fn_type(&self) -> bool {
317 match self.as_value() {
318 Some(value) => value == Constants::get().fn_type,
319 None => false,
320 }
321 }
322
323 pub(crate) fn is_fn_isinstance(&self) -> bool {
325 match self.as_value() {
326 Some(value) => value == Constants::get().fn_isinstance,
327 None => false,
328 }
329 }
330
331 pub(crate) fn as_type(&self) -> Option<&IrSpanned<ExprCompiled>> {
333 match self {
334 Self::Call(c) => c.as_type(),
335 _ => None,
336 }
337 }
338
339 pub(crate) fn as_type_is(&self) -> Option<(&IrSpanned<ExprCompiled>, FrozenStringValue)> {
341 match self {
342 ExprCompiled::Builtin1(Builtin1::TypeIs(t), x) => Some((x, *t)),
343 _ => None,
344 }
345 }
346
347 pub(crate) fn as_builtin_value(&self) -> Option<FrozenValue> {
349 match self {
350 Self::Value(x) if x.is_builtin() => Some(*x),
351 _ => None,
352 }
353 }
354
355 pub(crate) fn as_string(&self) -> Option<FrozenStringValue> {
357 FrozenStringValue::new(self.as_value()?)
358 }
359
360 pub(crate) fn is_iterable_empty(&self) -> bool {
362 match self {
363 ExprCompiled::List(xs) => xs.is_empty(),
364 ExprCompiled::Tuple(xs) => xs.is_empty(),
365 ExprCompiled::Dict(xs) => xs.is_empty(),
366 ExprCompiled::Value(v) if v.is_builtin() => {
367 v.to_value().length().map_or(false, |l| l == 0)
368 }
369 _ => false,
370 }
371 }
372
373 fn is_definitely_bool(&self) -> bool {
376 match self {
377 Self::Value(v) => v.unpack_bool().is_some(),
378 Self::Builtin1(Builtin1::Not | Builtin1::TypeIs(_), _)
379 | Self::Builtin2(Builtin2::In | Builtin2::Equals | Builtin2::Compare(_), ..) => true,
380 _ => false,
381 }
382 }
383
384 pub(crate) fn is_pure_infallible(&self) -> bool {
388 match self {
389 Self::Value(..) => true,
390 Self::List(xs) | Self::Tuple(xs) => xs.iter().all(|x| x.is_pure_infallible()),
391 Self::Dict(xs) => xs.is_empty(),
392 Self::Builtin1(Builtin1::Not | Builtin1::TypeIs(_), x) => x.is_pure_infallible(),
393 Self::Seq(x_y) => {
394 let (x, y) = &**x_y;
395 x.is_pure_infallible() && y.is_pure_infallible()
396 }
397 Self::LogicalBinOp(_op, x_y) => {
398 let (x, y) = &**x_y;
399 x.is_pure_infallible() && y.is_pure_infallible()
400 }
401 Self::If(cond_x_y) => {
402 let (cond, x, y) = &**cond_x_y;
403 cond.is_pure_infallible() && x.is_pure_infallible() && y.is_pure_infallible()
404 }
405 Self::Call(call) => call.is_pure_infallible(),
406 _ => false,
407 }
408 }
409
410 pub(crate) fn is_pure_infallible_to_bool(&self) -> Option<bool> {
413 match self {
414 ExprCompiled::Value(v) => Some(v.to_value().to_bool()),
415 ExprCompiled::List(xs) | ExprCompiled::Tuple(xs)
416 if xs.iter().all(|x| x.is_pure_infallible()) =>
417 {
418 Some(!xs.is_empty())
419 }
420 ExprCompiled::Dict(xs) if xs.is_empty() => Some(false),
422 ExprCompiled::Builtin1(Builtin1::Not, x) => x.is_pure_infallible_to_bool().map(|x| !x),
423 ExprCompiled::LogicalBinOp(op, x_y) => {
424 let (x, y) = &**x_y;
425 match (
426 op,
427 x.is_pure_infallible_to_bool(),
428 y.is_pure_infallible_to_bool(),
429 ) {
430 (ExprLogicalBinOp::And, Some(true), y) => y,
431 (ExprLogicalBinOp::Or, Some(false), y) => y,
432 (ExprLogicalBinOp::And, Some(false), _) => Some(false),
433 (ExprLogicalBinOp::Or, Some(true), _) => Some(true),
434 (_, None, _) => None,
435 }
436 }
437 _ => None,
438 }
439 }
440
441 pub(crate) fn as_local_non_captured(&self) -> Option<LocalSlotId> {
443 match self {
444 ExprCompiled::Local(slot) => Some(*slot),
445 _ => None,
446 }
447 }
448}
449
450enum ExprShortList<'a> {
451 Exprs(&'a [IrSpanned<ExprCompiled>]),
452 Constants(&'a [FrozenValue]),
453}
454
455impl<'a> IrSpanned<ExprShortList<'a>> {
456 fn as_exprs(&self) -> Vec<IrSpanned<ExprCompiled>> {
457 match &self.node {
458 ExprShortList::Exprs(exprs) => exprs.to_vec(),
459 ExprShortList::Constants(constants) => constants
460 .iter()
461 .map(|c| IrSpanned {
462 node: ExprCompiled::Value(*c),
463 span: self.span,
464 })
465 .collect(),
466 }
467 }
468}
469
470impl IrSpanned<ExprCompiled> {
471 fn as_short_list(&self) -> Option<IrSpanned<ExprShortList>> {
473 const MAX_LEN: usize = 1000;
475 match &self.node {
476 ExprCompiled::List(xs) if xs.len() <= MAX_LEN => Some(ExprShortList::Exprs(xs)),
477 ExprCompiled::Value(v) => {
478 let list = FrozenListData::from_frozen_value(v)?;
479 if list.len() <= MAX_LEN {
480 Some(ExprShortList::Constants(list.content()))
481 } else {
482 None
483 }
484 }
485 _ => None,
486 }
487 .map(|node| IrSpanned {
488 node,
489 span: self.span,
490 })
491 }
492
493 pub(crate) fn optimize(&self, ctx: &mut OptCtx) -> IrSpanned<ExprCompiled> {
494 let span = self.span;
495 let expr = match &self.node {
496 e @ (ExprCompiled::Value(..)
497 | ExprCompiled::Local(..)
498 | ExprCompiled::LocalCaptured(..)) => e.clone(),
499 ExprCompiled::Module(slot) => {
500 match ctx.frozen_module().and_then(|m| m.get_slot(*slot)) {
501 None => {
502 ExprCompiled::Module(*slot)
504 }
505 Some(v) => ExprCompiled::Value(v),
506 }
507 }
508 ExprCompiled::Tuple(xs) => {
509 ExprCompiled::tuple(xs.map(|e| e.optimize(ctx)), ctx.frozen_heap())
510 }
511 ExprCompiled::List(xs) => ExprCompiled::List(xs.map(|e| e.optimize(ctx))),
512 ExprCompiled::Dict(kvs) => {
513 ExprCompiled::Dict(kvs.map(|(k, v)| (k.optimize(ctx), v.optimize(ctx))))
514 }
515 ExprCompiled::Compr(compr) => compr.optimize(ctx),
516 ExprCompiled::If(cond_t_f) => {
517 let (cond, t, f) = &**cond_t_f;
518 let cond = cond.optimize(ctx);
519 let t = t.optimize(ctx);
520 let f = f.optimize(ctx);
521 return ExprCompiled::if_expr(cond, t, f);
522 }
523 ExprCompiled::Slice(v_start_stop_step) => {
524 let (v, start, stop, step) = &**v_start_stop_step;
525 let v = v.optimize(ctx);
526 let start = start.as_ref().map(|x| x.optimize(ctx));
527 let stop = stop.as_ref().map(|x| x.optimize(ctx));
528 let step = step.as_ref().map(|x| x.optimize(ctx));
529 ExprCompiled::slice(span, v, start, stop, step, ctx)
530 }
531 ExprCompiled::Builtin1(op, e) => {
532 let e = e.optimize(ctx);
533 ExprCompiled::un_op(span, op, e, ctx)
534 }
535 ExprCompiled::LogicalBinOp(op, l_r) => {
536 let (l, r) = &**l_r;
537 let l = l.optimize(ctx);
538 let r = r.optimize(ctx);
539 return ExprCompiled::logical_bin_op(*op, l, r);
540 }
541 ExprCompiled::Seq(l_r) => {
542 let (l, r) = &**l_r;
543 let l = l.optimize(ctx);
544 let r = r.optimize(ctx);
545 return ExprCompiled::seq(l, r);
546 }
547 ExprCompiled::Builtin2(op, l_r) => {
548 let (l, r) = &**l_r;
549 let l = l.optimize(ctx);
550 let r = r.optimize(ctx);
551 ExprCompiled::bin_op(*op, l, r, ctx)
552 }
553 ExprCompiled::Index2(a_i0_i1) => {
554 let (a, i0, i1) = &**a_i0_i1;
555 let a = a.optimize(ctx);
556 let i0 = i0.optimize(ctx);
557 let i1 = i1.optimize(ctx);
558 ExprCompiled::index2(a, i0, i1)
559 }
560 d @ ExprCompiled::Def(..) => (*d).clone(),
561 ExprCompiled::Call(ref call) => call.optimize(ctx),
562 };
563 IrSpanned { node: expr, span }
564 }
565}
566
567impl ExprCompiled {
568 fn equals(l: IrSpanned<ExprCompiled>, r: IrSpanned<ExprCompiled>) -> IrSpanned<ExprCompiled> {
569 let span = l.span.merge(&r.span);
570 if let (Some(l), Some(r)) = (l.as_value(), r.as_value()) {
571 if let Ok(r) = l.equals(r.to_value()) {
573 return IrSpanned {
574 span,
575 node: ExprCompiled::Value(FrozenValue::new_bool(r)),
576 };
577 }
578 }
579
580 let (l, r) = match try_eval_type_is(l, r) {
581 Ok(e) => return e,
582 Err((l, r)) => (l, r),
583 };
584
585 let (r, l) = match try_eval_type_is(r, l) {
586 Ok(e) => return e,
587 Err((r, l)) => (r, l),
588 };
589
590 IrSpanned {
591 span,
592 node: ExprCompiled::Builtin2(Builtin2::Equals, Box::new((l, r))),
593 }
594 }
595
596 pub(crate) fn not(span: FrameSpan, expr: IrSpanned<ExprCompiled>) -> IrSpanned<ExprCompiled> {
597 match expr.node {
598 ExprCompiled::Value(x) => IrSpanned {
599 node: ExprCompiled::Value(FrozenValue::new_bool(!x.to_value().to_bool())),
600 span,
601 },
602 ExprCompiled::Builtin1(Builtin1::Not, e) if e.is_definitely_bool() => (*e).clone(),
604 _ => IrSpanned {
605 node: ExprCompiled::Builtin1(Builtin1::Not, Box::new(expr)),
606 span,
607 },
608 }
609 }
610
611 fn or(l: IrSpanned<ExprCompiled>, r: IrSpanned<ExprCompiled>) -> IrSpanned<ExprCompiled> {
612 Self::logical_bin_op(ExprLogicalBinOp::Or, l, r)
613 }
614
615 fn and(l: IrSpanned<ExprCompiled>, r: IrSpanned<ExprCompiled>) -> IrSpanned<ExprCompiled> {
616 Self::logical_bin_op(ExprLogicalBinOp::And, l, r)
617 }
618
619 pub(crate) fn logical_bin_op(
620 op: ExprLogicalBinOp,
621 l: IrSpanned<ExprCompiled>,
622 r: IrSpanned<ExprCompiled>,
623 ) -> IrSpanned<ExprCompiled> {
624 if let Some(l_v) = l.is_pure_infallible_to_bool() {
625 if l_v == (op == ExprLogicalBinOp::Or) {
626 l
627 } else {
628 r
629 }
630 } else {
631 let span = l.span.merge(&r.span);
632 IrSpanned {
633 node: ExprCompiled::LogicalBinOp(op, Box::new((l, r))),
634 span,
635 }
636 }
637 }
638
639 pub(crate) fn seq(
640 l: IrSpanned<ExprCompiled>,
641 r: IrSpanned<ExprCompiled>,
642 ) -> IrSpanned<ExprCompiled> {
643 if l.is_pure_infallible() {
644 r
645 } else {
646 let span = l.span.merge(&r.span);
647 IrSpanned {
648 node: ExprCompiled::Seq(Box::new((l, r))),
649 span,
650 }
651 }
652 }
653
654 fn percent(
655 l: IrSpanned<ExprCompiled>,
656 r: IrSpanned<ExprCompiled>,
657 ctx: &mut OptCtx,
658 ) -> ExprCompiled {
659 if let Some(v) = l.as_string() {
660 if let Some((before, after)) = parse_percent_s_one(&v) {
661 let before = ctx.frozen_heap().alloc_str_intern(&before);
662 let after = ctx.frozen_heap().alloc_str_intern(&after);
663 return ExprCompiled::percent_s_one(before, r, after, ctx);
664 }
665 }
666 ExprCompiled::Builtin2(Builtin2::Percent, Box::new((l, r)))
667 }
668
669 fn percent_s_one(
670 before: FrozenStringValue,
671 arg: IrSpanned<ExprCompiled>,
672 after: FrozenStringValue,
673 ctx: &mut OptCtx,
674 ) -> ExprCompiled {
675 if let Some(arg) = arg.as_value() {
676 if let Ok(value) =
677 percent_s_one(before.as_str(), arg.to_value(), after.as_str(), ctx.heap())
678 {
679 let value = ctx.frozen_heap().alloc_str_intern(value.as_str());
680 return ExprCompiled::Value(value.to_frozen_value());
681 }
682 }
683
684 ExprCompiled::Builtin1(Builtin1::PercentSOne(before, after), Box::new(arg))
685 }
686
687 pub(crate) fn format_one(
688 before: FrozenStringValue,
689 arg: IrSpanned<ExprCompiled>,
690 after: FrozenStringValue,
691 ctx: &mut OptCtx,
692 ) -> ExprCompiled {
693 if let Some(arg) = arg.as_value() {
694 let value = format_one(&before, arg.to_value(), &after, ctx.heap());
695 let value = ctx.frozen_heap().alloc_str_intern(value.as_str());
696 return ExprCompiled::Value(value.to_frozen_value());
697 }
698
699 ExprCompiled::Builtin1(Builtin1::FormatOne(before, after), Box::new(arg))
700 }
701
702 fn add(l: IrSpanned<ExprCompiled>, r: IrSpanned<ExprCompiled>) -> ExprCompiled {
703 if let (Some(l), Some(r)) = (l.as_short_list(), r.as_short_list()) {
704 return ExprCompiled::List(l.as_exprs().into_iter().chain(r.as_exprs()).collect());
705 }
706 ExprCompiled::Builtin2(Builtin2::Add, Box::new((l, r)))
707 }
708
709 pub(crate) fn bin_op(
710 bin_op: Builtin2,
711 l: IrSpanned<ExprCompiled>,
712 r: IrSpanned<ExprCompiled>,
713 ctx: &mut OptCtx,
714 ) -> ExprCompiled {
715 let span = l.span.merge(&r.span);
716 if let (Some(l), Some(r)) = (l.as_builtin_value(), r.as_builtin_value()) {
719 if let Ok(v) = bin_op.eval(l.to_value(), r.to_value(), ctx.heap()) {
720 if let Some(v) = ExprCompiled::try_value(span, v, ctx.frozen_heap()) {
721 return v;
722 }
723 }
724 }
725
726 match bin_op {
727 Builtin2::Percent => ExprCompiled::percent(l, r, ctx),
728 Builtin2::Add => ExprCompiled::add(l, r),
729 Builtin2::Equals => ExprCompiled::equals(l, r).node,
730 Builtin2::ArrayIndex => ExprCompiled::index(l, r, ctx),
731 bin_op => ExprCompiled::Builtin2(bin_op, Box::new((l, r))),
732 }
733 }
734
735 pub(crate) fn if_expr(
736 cond: IrSpanned<ExprCompiled>,
737 t: IrSpanned<ExprCompiled>,
738 f: IrSpanned<ExprCompiled>,
739 ) -> IrSpanned<ExprCompiled> {
740 let cond_span = cond.span;
741 let cond = ExprCompiledBool::new(cond);
742 match cond.node {
743 ExprCompiledBool::Const(true) => t,
744 ExprCompiledBool::Const(false) => f,
745 ExprCompiledBool::Expr(cond) => match cond {
746 ExprCompiled::Builtin1(Builtin1::Not, cond) => ExprCompiled::if_expr(*cond, f, t),
747 ExprCompiled::Seq(x_cond) => {
748 let (x, cond) = *x_cond;
749 ExprCompiled::seq(x, ExprCompiled::if_expr(cond, t, f))
750 }
751 cond => {
752 let cond = IrSpanned {
753 node: cond,
754 span: cond_span,
755 };
756 let span = cond.span.merge(&t.span).merge(&f.span);
757 IrSpanned {
758 node: ExprCompiled::If(Box::new((cond, t, f))),
759 span,
760 }
761 }
762 },
763 }
764 }
765
766 pub(crate) fn un_op(
767 span: FrameSpan,
768 op: &Builtin1,
769 expr: IrSpanned<ExprCompiled>,
770 ctx: &mut OptCtx,
771 ) -> ExprCompiled {
772 if let Some(v) = expr.as_builtin_value() {
773 if let Some(v) = op.eval(v, ctx) {
774 if let Some(v) = ExprCompiled::try_value(expr.span, v, ctx.frozen_heap()) {
775 return v;
776 }
777 }
778 }
779 match op {
780 Builtin1::FormatOne(before, after) => {
781 ExprCompiled::format_one(*before, expr, *after, ctx)
782 }
783 Builtin1::PercentSOne(before, after) => {
784 ExprCompiled::percent_s_one(*before, expr, *after, ctx)
785 }
786 Builtin1::Dot(field) => ExprCompiled::dot(expr, field, ctx),
787 Builtin1::TypeIs(t) => ExprCompiled::type_is(expr, *t),
788 Builtin1::Not => ExprCompiled::not(span, expr).node,
789 op => ExprCompiled::Builtin1(op.clone(), Box::new(expr)),
790 }
791 }
792
793 fn try_values(
794 span: FrameSpan,
795 values: &[Value],
796 heap: &FrozenHeap,
797 ) -> Option<Vec<IrSpanned<ExprCompiled>>> {
798 values
799 .try_map(|v| {
800 Self::try_value(span, *v, heap)
801 .map(|expr| IrSpanned { span, node: expr })
802 .ok_or(())
803 })
804 .ok()
805 }
806
807 pub(crate) fn try_value(span: FrameSpan, v: Value, heap: &FrozenHeap) -> Option<ExprCompiled> {
809 if let Some(v) = v.unpack_frozen() {
810 Some(ExprCompiled::Value(v))
812 } else if let Some(v) = v.unpack_str() {
813 if v.len() <= 1000 {
814 Some(ExprCompiled::Value(
816 heap.alloc_str_intern(v).to_frozen_value(),
817 ))
818 } else {
819 None
822 }
823 } else if let Some(v) = v.downcast_ref::<StarlarkFloat>() {
824 Some(ExprCompiled::Value(heap.alloc(*v)))
825 } else if let Some(v) = v.downcast_ref::<Range>() {
826 Some(ExprCompiled::Value(heap.alloc(*v)))
827 } else if let Some(v) = ListRef::from_value(v) {
828 let items = Self::try_values(span, v.content(), heap)?;
831 Some(ExprCompiled::List(items))
832 } else if let Some(v) = Tuple::from_value(v) {
833 let items = Self::try_values(span, v.content(), heap)?;
834 Some(Self::tuple(items, heap))
835 } else {
836 None
837 }
838 }
839
840 pub(crate) fn compr(compr: ComprCompiled) -> ExprCompiled {
841 match compr {
842 ComprCompiled::List(x, clauses) => {
843 if clauses.is_nop() {
844 ExprCompiled::List(Vec::new())
845 } else {
846 ExprCompiled::Compr(ComprCompiled::List(x, clauses))
847 }
848 }
849 ComprCompiled::Dict(k_v, clauses) => {
850 let (k, v) = *k_v;
851 if clauses.is_nop() {
852 ExprCompiled::Dict(Vec::new())
853 } else {
854 ExprCompiled::Compr(ComprCompiled::Dict(Box::new((k, v)), clauses))
855 }
856 }
857 }
858 }
859
860 pub(crate) fn tuple(elems: Vec<IrSpanned<ExprCompiled>>, heap: &FrozenHeap) -> ExprCompiled {
862 if let Ok(elems) = elems.try_map(|e| e.as_value().ok_or(())) {
863 ExprCompiled::Value(heap.alloc_tuple(&elems))
864 } else {
865 ExprCompiled::Tuple(elems)
866 }
867 }
868
869 pub(crate) fn compile_time_getattr(
870 left: FrozenValue,
871 attr: &Symbol,
872 ctx: &mut OptCtx,
873 ) -> Option<FrozenValue> {
874 let v = get_attr_hashed_raw(left.to_value(), attr, ctx.heap()).ok()?;
876 match v {
877 MemberOrValue::Member(m) => match m {
878 UnboundValue::Method(m, _) => Some(
879 ctx.frozen_heap()
880 .alloc_simple(BoundMethodGen::new(left, *m)),
881 ),
882 UnboundValue::Attr(..) => None,
883 },
884 MemberOrValue::Value(v) => v.unpack_frozen(),
885 }
886 }
887
888 pub(crate) fn dot(
889 object: IrSpanned<ExprCompiled>,
890 field: &Symbol,
891 ctx: &mut OptCtx,
892 ) -> ExprCompiled {
893 if let Some(left) = object.as_value() {
894 if let Some(v) = Self::compile_time_getattr(left, field, ctx) {
895 return ExprCompiled::Value(v);
896 }
897 }
898
899 ExprCompiled::Builtin1(Builtin1::Dot(field.clone()), Box::new(object))
900 }
901
902 fn slice(
903 span: FrameSpan,
904 array: IrSpanned<ExprCompiled>,
905 start: Option<IrSpanned<ExprCompiled>>,
906 stop: Option<IrSpanned<ExprCompiled>>,
907 step: Option<IrSpanned<ExprCompiled>>,
908 ctx: &mut OptCtx,
909 ) -> ExprCompiled {
910 if let (Some(array), Some(start), Some(stop), Some(step)) = (
911 array.as_builtin_value(),
912 start.as_ref().map(|e| e.as_value()),
913 stop.as_ref().map(|e| e.as_value()),
914 step.as_ref().map(|e| e.as_value()),
915 ) {
916 if let Ok(v) = array.to_value().slice(
917 start.map(|v| v.to_value()),
918 stop.map(|v| v.to_value()),
919 step.map(|v| v.to_value()),
920 ctx.heap(),
921 ) {
922 if let Some(v) = ExprCompiled::try_value(span, v, ctx.frozen_heap()) {
923 return v;
924 }
925 }
926 }
927 ExprCompiled::Slice(Box::new((array, start, stop, step)))
928 }
929
930 pub(crate) fn index(
931 array: IrSpanned<ExprCompiled>,
932 index: IrSpanned<ExprCompiled>,
933 ctx: &mut OptCtx,
934 ) -> ExprCompiled {
935 let span = array.span.merge(&index.span);
936 if let (Some(array), Some(index)) = (array.as_builtin_value(), index.as_value()) {
937 if let Ok(v) = array.to_value().at(index.to_value(), ctx.heap()) {
938 if let Some(expr) = ExprCompiled::try_value(span, v, ctx.frozen_heap()) {
939 return expr;
940 }
941 }
942 }
943 ExprCompiled::Builtin2(Builtin2::ArrayIndex, Box::new((array, index)))
944 }
945
946 pub(crate) fn index2(
947 array: IrSpanned<ExprCompiled>,
948 index0: IrSpanned<ExprCompiled>,
949 index1: IrSpanned<ExprCompiled>,
950 ) -> ExprCompiled {
951 ExprCompiled::Index2(Box::new((array, index0, index1)))
952 }
953
954 pub(crate) fn typ(span: FrameSpan, v: IrSpanned<ExprCompiled>) -> ExprCompiled {
955 match &v.node {
956 ExprCompiled::Value(v) => {
957 ExprCompiled::Value(v.to_value().get_type_value().to_frozen_value())
958 }
959 ExprCompiled::Tuple(xs) if xs.iter().all(|e| e.is_pure_infallible()) => {
960 ExprCompiled::Value(Tuple::get_type_value_static().to_frozen_value())
961 }
962 ExprCompiled::List(xs) if xs.iter().all(|e| e.is_pure_infallible()) => {
963 ExprCompiled::Value(ListData::get_type_value_static().to_frozen_value())
964 }
965 ExprCompiled::Dict(xs) if xs.is_empty() => {
966 ExprCompiled::Value(Dict::get_type_value_static().to_frozen_value())
967 }
968 ExprCompiled::Builtin1(Builtin1::Not | Builtin1::TypeIs(_), x)
969 if x.is_pure_infallible() =>
970 {
971 ExprCompiled::Value(StarlarkBool::get_type_value_static().to_frozen_value())
972 }
973 _ => ExprCompiled::Call(Box::new(IrSpanned {
974 span,
975 node: CallCompiled {
976 fun: IrSpanned {
977 span,
978 node: ExprCompiled::Value(Constants::get().fn_type.0),
979 },
980 args: ArgsCompiledValue {
981 pos_named: vec![v],
982 ..ArgsCompiledValue::default()
983 },
984 },
985 })),
986 }
987 }
988
989 pub(crate) fn type_is(v: IrSpanned<ExprCompiled>, t: FrozenStringValue) -> ExprCompiled {
990 if let Some(v) = v.as_value() {
991 return ExprCompiled::Value(FrozenValue::new_bool(
992 v.to_value().get_type() == t.as_str(),
993 ));
994 }
995 ExprCompiled::Builtin1(Builtin1::TypeIs(t), Box::new(v))
996 }
997
998 pub(crate) fn len(span: FrameSpan, arg: IrSpanned<ExprCompiled>) -> ExprCompiled {
999 if let Some(arg) = arg.as_value() {
1000 if let Ok(len) = arg.to_value().length() {
1001 if let Ok(len) = InlineInt::try_from(len) {
1002 return ExprCompiled::Value(FrozenValue::new_int(len));
1003 }
1004 }
1005 }
1006 ExprCompiled::Call(Box::new(IrSpanned {
1007 span,
1008 node: CallCompiled {
1009 fun: IrSpanned {
1010 span,
1011 node: ExprCompiled::Value(Constants::get().fn_len.0),
1012 },
1013 args: ArgsCompiledValue {
1014 pos_named: vec![arg],
1015 ..ArgsCompiledValue::default()
1016 },
1017 },
1018 }))
1019 }
1020}
1021
1022#[derive(Debug, Clone, Error)]
1023pub(crate) enum EvalError {
1024 #[error("Dictionary key repeated for `{0}`")]
1025 DuplicateDictionaryKey(String),
1026}
1027
1028fn try_eval_type_is(
1031 l: IrSpanned<ExprCompiled>,
1032 r: IrSpanned<ExprCompiled>,
1033) -> Result<IrSpanned<ExprCompiled>, (IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)> {
1034 let span = l.span.merge(&r.span);
1035 if let (Some(l), Some(r)) = (l.as_type(), r.as_string()) {
1036 Ok(IrSpanned {
1037 span,
1038 node: ExprCompiled::type_is(l.clone(), r),
1039 })
1040 } else {
1041 Err((l, r))
1042 }
1043}
1044
1045trait AstLiteralCompile {
1046 fn compile(&self, heap: &FrozenHeap) -> FrozenValue;
1047}
1048
1049impl AstLiteralCompile for AstLiteral {
1050 fn compile(&self, heap: &FrozenHeap) -> FrozenValue {
1051 match self {
1052 AstLiteral::Int(i) => heap.alloc(StarlarkInt::from(i.node.clone())),
1053 AstLiteral::Float(f) => heap.alloc(f.node),
1054 AstLiteral::String(x) => heap.alloc(x.node.as_str()),
1055 AstLiteral::Ellipsis => heap.alloc(Ellipsis),
1056 }
1057 }
1058}
1059
1060trait CompilerExprUtil<P: AstPayload> {
1061 fn unpack_string_literal(&self) -> Option<&str>;
1062 fn reduces_to_string<'a>(
1063 op: BinOp,
1064 left: &'a AstExprP<P>,
1065 right: &'a AstExprP<P>,
1066 ) -> Option<String>;
1067}
1068
1069impl<P: AstPayload> CompilerExprUtil<P> for ExprP<P> {
1070 fn unpack_string_literal(&self) -> Option<&str> {
1071 match self {
1072 ExprP::Literal(AstLiteral::String(i)) => Some(&i.node),
1073 _ => None,
1074 }
1075 }
1076
1077 fn reduces_to_string<'a>(
1079 mut op: BinOp,
1080 mut left: &'a AstExprP<P>,
1081 mut right: &'a AstExprP<P>,
1082 ) -> Option<String> {
1083 let mut results = Vec::new();
1084 loop {
1085 if op != BinOp::Add {
1086 return None;
1087 }
1088 let x = right.unpack_string_literal()?;
1090 results.push(x.to_owned());
1091 match &left.node {
1092 ExprP::Op(left2, op2, right2) => {
1093 op = *op2;
1094 left = left2;
1095 right = right2;
1096 }
1097 _ => {
1098 let x = left.unpack_string_literal()?;
1099 results.push(x.to_owned());
1100 break;
1101 }
1102 }
1103 }
1104 results.reverse();
1105 Some(results.concat())
1106 }
1107}
1108
1109#[cold]
1110#[inline(never)]
1111fn get_attr_no_attr_error<'v>(x: Value<'v>, attribute: &Symbol) -> crate::Error {
1112 match did_you_mean(attribute.as_str(), x.dir_attr().iter().map(|s| s.as_str())) {
1113 None => ValueError::NoAttr(x.get_type().to_owned(), attribute.as_str().to_owned()).into(),
1114 Some(better) => ValueError::NoAttrDidYouMean(
1115 x.get_type().to_owned(),
1116 attribute.as_str().to_owned(),
1117 better.to_owned(),
1118 )
1119 .into(),
1120 }
1121}
1122
1123pub(crate) enum MemberOrValue<'v, 'a> {
1124 Member(&'a UnboundValue),
1125 Value(Value<'v>),
1126}
1127
1128impl<'v, 'a> MemberOrValue<'v, 'a> {
1129 #[inline]
1130 pub(crate) fn invoke(
1131 &self,
1132 this: Value<'v>,
1133 span: FrozenRef<'static, FrameSpan>,
1134 args: &Arguments<'v, '_>,
1135 eval: &mut Evaluator<'v, '_, '_>,
1136 ) -> crate::Result<Value<'v>> {
1137 match self {
1138 MemberOrValue::Member(member) => member.invoke_method(this, span, args, eval),
1139 MemberOrValue::Value(value) => value.invoke_with_loc(Some(span), args, eval),
1140 }
1141 }
1142}
1143
1144#[inline(always)]
1145pub(crate) fn get_attr_hashed_raw<'v>(
1146 x: Value<'v>,
1147 attribute: &Symbol,
1148 heap: &'v Heap,
1149) -> crate::Result<MemberOrValue<'v, 'static>> {
1150 let aref = x.get_ref();
1151 if let Some(methods) = aref.vtable().methods() {
1152 if let Some(v) = methods.get_frozen_symbol(attribute) {
1153 return Ok(MemberOrValue::Member(v));
1154 }
1155 }
1156 match aref.get_attr_hashed(attribute.as_str_hashed(), heap) {
1157 None => Err(get_attr_no_attr_error(x, attribute)),
1158 Some(x) => Ok(MemberOrValue::Value(x)),
1159 }
1160}
1161
1162pub(crate) fn get_attr_hashed_bind<'v>(
1163 x: Value<'v>,
1164 attribute: &Symbol,
1165 heap: &'v Heap,
1166) -> crate::Result<Value<'v>> {
1167 let aref = x.get_ref();
1168 if let Some(methods) = aref.vtable().methods() {
1169 if let Some(v) = methods.get_frozen_symbol(attribute) {
1170 return v.bind(x, heap);
1171 }
1172 }
1173 match aref.get_attr_hashed(attribute.as_str_hashed(), heap) {
1174 None => Err(get_attr_no_attr_error(x, attribute)),
1175 Some(x) => {
1176 Ok(x)
1179 }
1180 }
1181}
1182
1183impl<'v, 'a, 'e> Compiler<'v, 'a, 'e, '_> {
1184 fn expr_ident(&mut self, ident: &CstIdent) -> ExprCompiled {
1185 let resolved_ident = ident
1186 .node
1187 .payload
1188 .as_ref()
1189 .unwrap_or_else(|| panic!("variable not resolved: `{}`", ident.node.ident));
1190 match resolved_ident {
1191 ResolvedIdent::Slot(Slot::Local(slot), binding_id) => {
1192 let binding = self.scope_data.get_binding(*binding_id);
1193
1194 match binding.captured {
1197 Captured::Yes => ExprCompiled::LocalCaptured(LocalCapturedSlotId(slot.0)),
1198 Captured::No => ExprCompiled::Local(LocalSlotId(slot.0)),
1199 }
1200 }
1201 ResolvedIdent::Slot(Slot::Module(slot), binding_id) => {
1202 let binding = self.scope_data.get_binding(*binding_id);
1203
1204 if binding.assign_count == AssignCount::AtMostOnce {
1207 if let Some(v) = self.eval.module_env.slots().get_slot(*slot) {
1208 if let Some(v) = v.unpack_frozen() {
1211 return ExprCompiled::Value(v);
1212 }
1213 }
1214 }
1215
1216 ExprCompiled::Module(*slot)
1217 }
1218 ResolvedIdent::Global(v) => ExprCompiled::Value(*v),
1219 }
1220 }
1221
1222 fn opt_ctx<'s>(&'s mut self) -> OptCtx<'v, 'a, 'e, 's> {
1223 let param_count = self.current_scope().param_count();
1224 OptCtx::new(self.eval, param_count)
1225 }
1226
1227 pub(crate) fn expr(
1228 &mut self,
1229 expr: &CstExpr,
1230 ) -> Result<IrSpanned<ExprCompiled>, CompilerInternalError> {
1231 let span = FrameSpan::new(FrozenFileSpan::new(self.codemap, expr.span));
1233 let expr = match &expr.node {
1234 ExprP::Identifier(ident) => self.expr_ident(ident),
1235 ExprP::Lambda(l) => {
1236 let signature_span = l.signature_span();
1237 let signature_span = FrozenFileSpan::new(self.codemap, signature_span);
1238 let LambdaP {
1239 params,
1240 body,
1241 payload: scope_id,
1242 } = l;
1243 let suite = Spanned {
1244 span: expr.span,
1245 node: StmtP::Return(Some(*body.clone())),
1247 };
1248 self.function("lambda", signature_span, *scope_id, params, None, &suite)?
1249 }
1250 ExprP::Tuple(exprs) => {
1251 let xs = self.exprs(exprs)?;
1252 ExprCompiled::tuple(xs, self.eval.module_env.frozen_heap())
1253 }
1254 ExprP::List(exprs) => {
1255 let xs = self.exprs(exprs)?;
1256 ExprCompiled::List(xs)
1257 }
1258 ExprP::Dict(exprs) => {
1259 let xs = exprs
1260 .iter()
1261 .map(|(k, v)| Ok((self.expr(k)?, self.expr(v)?)))
1262 .collect::<Result<_, CompilerInternalError>>()?;
1263 ExprCompiled::Dict(xs)
1264 }
1265 ExprP::If(cond_then_expr_else_expr) => {
1266 let (cond, then_expr, else_expr) = &**cond_then_expr_else_expr;
1267 let cond = self.expr(cond)?;
1268 let then_expr = self.expr(then_expr)?;
1269 let else_expr = self.expr(else_expr)?;
1270 return Ok(ExprCompiled::if_expr(cond, then_expr, else_expr));
1271 }
1272 ExprP::Dot(left, right) => {
1273 let left = self.expr(left)?;
1274 let s = Symbol::new(&right.node);
1275
1276 ExprCompiled::dot(left, &s, &mut self.opt_ctx())
1277 }
1278 ExprP::Call(left, args) => {
1279 let left = self.expr(left)?;
1280 let args = self.args(args)?;
1281 CallCompiled::call(span, left, args, &mut self.opt_ctx())
1282 }
1283 ExprP::Index(array_index) => {
1284 let (array, index) = &**array_index;
1285 let array = self.expr(array)?;
1286 let index = self.expr(index)?;
1287 ExprCompiled::index(array, index, &mut self.opt_ctx())
1288 }
1289 ExprP::Index2(array_index0_index1) => {
1290 let (array, index0, index1) = &**array_index0_index1;
1291 let array = self.expr(array)?;
1292 let index0 = self.expr(index0)?;
1293 let index1 = self.expr(index1)?;
1294 ExprCompiled::index2(array, index0, index1)
1295 }
1296 ExprP::Slice(collection, start, stop, stride) => {
1297 let collection = self.expr(collection)?;
1298 let start = start.as_ref().map(|x| self.expr(x)).transpose()?;
1299 let stop = stop.as_ref().map(|x| self.expr(x)).transpose()?;
1300 let stride = stride.as_ref().map(|x| self.expr(x)).transpose()?;
1301 ExprCompiled::slice(span, collection, start, stop, stride, &mut self.opt_ctx())
1302 }
1303 ExprP::Not(expr) => {
1304 let expr = self.expr(expr)?;
1305 return Ok(ExprCompiled::not(span, expr));
1306 }
1307 ExprP::Minus(expr) => {
1308 let expr = self.expr(expr)?;
1309 ExprCompiled::un_op(span, &Builtin1::Minus, expr, &mut self.opt_ctx())
1310 }
1311 ExprP::Plus(expr) => {
1312 let expr = self.expr(expr)?;
1313 ExprCompiled::un_op(span, &Builtin1::Plus, expr, &mut self.opt_ctx())
1314 }
1315 ExprP::BitNot(expr) => {
1316 let expr = self.expr(expr)?;
1317 ExprCompiled::un_op(span, &Builtin1::BitNot, expr, &mut self.opt_ctx())
1318 }
1319 ExprP::Op(left, op, right) => {
1320 if let Some(x) = ExprP::reduces_to_string(*op, left, right) {
1321 let val = self.eval.module_env.frozen_heap().alloc(x);
1325 ExprCompiled::Value(val)
1326 } else {
1327 let right = if *op == BinOp::In || *op == BinOp::NotIn {
1328 list_to_tuple(right)
1329 } else {
1330 Cow::Borrowed(&**right)
1331 };
1332
1333 let l = self.expr(left)?;
1334 let r = self.expr(&right)?;
1335 match op {
1336 BinOp::Or => return Ok(ExprCompiled::or(l, r)),
1337 BinOp::And => return Ok(ExprCompiled::and(l, r)),
1338 BinOp::Equal => return Ok(ExprCompiled::equals(l, r)),
1339 BinOp::NotEqual => {
1340 return Ok(ExprCompiled::not(span, ExprCompiled::equals(l, r)));
1341 }
1342 BinOp::Less => ExprCompiled::bin_op(
1343 Builtin2::Compare(CompareOp::Less),
1344 l,
1345 r,
1346 &mut self.opt_ctx(),
1347 ),
1348 BinOp::Greater => ExprCompiled::bin_op(
1349 Builtin2::Compare(CompareOp::Greater),
1350 l,
1351 r,
1352 &mut self.opt_ctx(),
1353 ),
1354 BinOp::LessOrEqual => ExprCompiled::bin_op(
1355 Builtin2::Compare(CompareOp::LessOrEqual),
1356 l,
1357 r,
1358 &mut self.opt_ctx(),
1359 ),
1360 BinOp::GreaterOrEqual => ExprCompiled::bin_op(
1361 Builtin2::Compare(CompareOp::GreaterOrEqual),
1362 l,
1363 r,
1364 &mut self.opt_ctx(),
1365 ),
1366 BinOp::In => ExprCompiled::bin_op(Builtin2::In, l, r, &mut self.opt_ctx()),
1367 BinOp::NotIn => {
1368 ExprCompiled::not(
1369 span,
1370 IrSpanned {
1371 span,
1372 node: ExprCompiled::bin_op(
1373 Builtin2::In,
1374 l,
1375 r,
1376 &mut self.opt_ctx(),
1377 ),
1378 },
1379 )
1380 .node
1381 }
1382 BinOp::Subtract => {
1383 ExprCompiled::bin_op(Builtin2::Sub, l, r, &mut self.opt_ctx())
1384 }
1385 BinOp::Add => {
1386 ExprCompiled::bin_op(Builtin2::Add, l, r, &mut self.opt_ctx())
1387 }
1388 BinOp::Multiply => {
1389 ExprCompiled::bin_op(Builtin2::Multiply, l, r, &mut self.opt_ctx())
1390 }
1391 BinOp::Percent => {
1392 ExprCompiled::bin_op(Builtin2::Percent, l, r, &mut self.opt_ctx())
1393 }
1394 BinOp::Divide => {
1395 ExprCompiled::bin_op(Builtin2::Divide, l, r, &mut self.opt_ctx())
1396 }
1397 BinOp::FloorDivide => {
1398 ExprCompiled::bin_op(Builtin2::FloorDivide, l, r, &mut self.opt_ctx())
1399 }
1400 BinOp::BitAnd => {
1401 ExprCompiled::bin_op(Builtin2::BitAnd, l, r, &mut self.opt_ctx())
1402 }
1403 BinOp::BitOr => {
1404 ExprCompiled::bin_op(Builtin2::BitOr, l, r, &mut self.opt_ctx())
1405 }
1406 BinOp::BitXor => {
1407 ExprCompiled::bin_op(Builtin2::BitXor, l, r, &mut self.opt_ctx())
1408 }
1409 BinOp::LeftShift => {
1410 ExprCompiled::bin_op(Builtin2::LeftShift, l, r, &mut self.opt_ctx())
1411 }
1412 BinOp::RightShift => {
1413 ExprCompiled::bin_op(Builtin2::RightShift, l, r, &mut self.opt_ctx())
1414 }
1415 }
1416 }
1417 }
1418 ExprP::ListComprehension(x, for_, clauses) => {
1419 self.list_comprehension(x, for_, clauses)?
1420 }
1421 ExprP::DictComprehension(k_v, for_, clauses) => {
1422 let (k, v) = &**k_v;
1423 self.dict_comprehension(k, v, for_, clauses)?
1424 }
1425 ExprP::Literal(x) => {
1426 let val = x.compile(self.eval.module_env.frozen_heap());
1427 ExprCompiled::Value(val)
1428 }
1429 ExprP::FString(fstring) => {
1430 let Spanned {
1431 node:
1432 FStringP {
1433 format,
1434 expressions,
1435 },
1436 span: fstring_span,
1437 } = fstring;
1438
1439 let fstring_span = FrameSpan::new(FrozenFileSpan::new(self.codemap, *fstring_span));
1440
1441 let heap = self.eval.module_env.frozen_heap();
1443
1444 let format = IrSpanned {
1445 node: ExprCompiled::Value(heap.alloc(format.node.as_str())),
1446 span: fstring_span,
1447 };
1448 let method = IrSpanned {
1449 node: ExprCompiled::dot(format, &Symbol::new("format"), &mut self.opt_ctx()),
1450 span: fstring_span,
1451 };
1452
1453 let mut args = ArgsCompiledValue::default();
1454 for expr in expressions {
1455 args.push_pos(self.expr(expr)?);
1456 }
1457
1458 CallCompiled::call(span, method, args, &mut self.opt_ctx())
1459 }
1460 };
1461 Ok(IrSpanned { node: expr, span })
1462 }
1463
1464 pub(crate) fn expr_truth(
1467 &mut self,
1468 expr: &CstExpr,
1469 ) -> Result<IrSpanned<ExprCompiledBool>, CompilerInternalError> {
1470 let expr = self.expr(expr)?;
1471 Ok(ExprCompiledBool::new(expr))
1472 }
1473
1474 pub(crate) fn exprs(
1475 &mut self,
1476 exprs: &[CstExpr],
1477 ) -> Result<Vec<IrSpanned<ExprCompiled>>, CompilerInternalError> {
1478 exprs
1479 .iter()
1480 .map(|e| self.expr(e))
1481 .collect::<Result<_, CompilerInternalError>>()
1482 }
1483}