///|
/// Applies an augmented-assignment operator to `lv` and `rv`. `+=` on a list and
/// `|=` on a dict mutate the left operand in place (so aliases observe the
/// change); every other operator delegates to the corresponding binary
/// operation. Shared by the VM's `AugApply` opcode.
fn eval_aug_val(
  ctx : EvalContext,
  lv : @value.Value,
  aug_op : @syntax.AugOp,
  rv : @value.Value,
  pos : @errors.Position,
) -> @value.Value raise EvalErr {
  match (lv, aug_op) {
    (@value.Value::List(l), @syntax.AugOp::AugAdd) =>
      if rv is @value.Value::List(src) {
        let snapshot = src.copy_items()
        check(ctx, l.check_mutable("apply += to"))
        for item in snapshot {
          l.push(item) |> ignore
        }
        lv
      } else {
        match @value.iterate(rv) {
          Ok(iter) => {
            match l.check_mutable("apply += to") {
              Err(msg) => {
                iter.done()
                raise EvalErr(make_eval_error(ctx, msg))
              }
              Ok(_) => ()
            }
            while true {
              match iter.next() {
                None => break
                Some(item) => l.push(item) |> ignore
              }
            }
            iter.done()
            lv
          }
          Err(_) => eval_binary(ctx, lv, @syntax.BinaryOp::OpAdd, rv, pos)
        }
      }
    (@value.Value::Dict(ld), @syntax.AugOp::AugBitOr) =>
      match rv {
        @value.Value::Dict(rd) => {
          check(ctx, ld.check_mutable("apply |= to"))
          let pairs : Array[(@value.Value, @value.Value)] = []
          rd.each(fn(k, v) { pairs.push((k, v)) })
          for pair in pairs {
            let (k, v) = pair
            ignore(check(ctx, ld.set(k, v)))
          }
          lv
        }
        _ =>
          raise EvalErr(
            make_eval_error(ctx, "unknown binary op: dict | \{rv.type_name()}"),
          )
      }
    _ => {
      let bin_op = aug_to_bin_op(aug_op)
      eval_binary(ctx, lv, bin_op, rv, pos)
    }
  }
}