// Reading the value an instruction produced.
//
// Ported from wax/src/lib-wax/typing.ml.
//
// Every checked instruction carries the types of the values it leaves on the
// stack -- an array, because an instruction may leave none or several. Asking
// for THE value it produced is therefore a question that can fail, and failing
// is a diagnostic rather than an option the caller inspects. That makes this a
// query with a reporting side effect, which is why it needs the suppression
// below.

///|
/// The single value an instruction produced.
///
/// An instruction that left no value, or several, is not an expression, and
/// this says so and recovers with `Error` poison -- so a caller that asked in
/// vain still has something to carry on with.
///
/// Reported ONCE per rendered position and count. One node is legitimately
/// asked by several consumers -- a call's callee twice, a labelled block as
/// both value and statement -- and nested value-less expressions share a start
/// column (`a.m().m()`, both halves value-less), so a full-span key would let
/// two identical reports print at the same place. Keying on what the reader
/// actually sees -- the start column and the count -- keeps a genuine second
/// error with a different count.
pub fn expression_type(
  ctx : @typing_env.ModuleContext,
  annotation : @typing_env.InferredAnnotation,
) -> @infer.Cell[@infer.InferredType] {
  let (types, location) = annotation
  if types.length() == 1 {
    return types[0]
  }
  let key = (location.start.cnum, types.length())
  if !ctx.not_expression_reported.contains(key) {
    ctx.not_expression_reported[key] = ()
    // An unresolved label in this function makes the value shape unreliable: a
    // block whose only value delivery was the unresolved branch legitimately
    // computes nothing, and saying so would anchor a derived error away from
    // the unbound label.
    if !ctx.unresolved_label.val {
      not_an_expression(ctx.diagnostics, location, types.length())
    }
  }
  @infer.Cell::make(@infer.InferredType::Error)
}

///|
/// The type of a literal, from its spelling alone.
///
/// These are the arms of the instruction match that need nothing but the token:
/// no operands, no name to resolve, no recursion. They commit only as far as
/// the spelling requires, which is what lets `1` become an `i64` where one is
/// wanted and an `f32` where one is.
pub fn literal_type(
  desc : @ast.InstrDesc[@basic.Location],
) -> @infer.InferredType? {
  match desc {
    // A character is its code point, which is an i32 and nothing else.
    Char(_) => Some(Valtype(@infer.i32_valtype))
    Int(s) => Some(int_literal_lattice(s))
    Float(s) => Some(float_literal_lattice(s))
    // A bare `null` is the null reference: it belongs to every reference type
    // and picks one from whatever it is used as.
    Null => Some(Null)
    _ => None
  }
}