#![allow(dead_code)]
use shape_ast::ast::{Expr, Literal, ObjectEntry, Span, TypeAnnotation};
use shape_value::v2::ConcreteType;
use super::BytecodeCompiler;
impl BytecodeCompiler {
pub(crate) fn record_map_key_value_for_node(
&mut self,
span: Span,
key: ConcreteType,
value: ConcreteType,
) {
self.map_key_value_types.insert(span, (key, value));
}
pub(crate) fn record_map_key_value_for_local(
&mut self,
slot: u16,
key: ConcreteType,
value: ConcreteType,
) {
self.local_map_key_value_types.insert(slot, (key, value));
}
pub(crate) fn record_map_key_value_for_module_binding(
&mut self,
binding_idx: u16,
key: ConcreteType,
value: ConcreteType,
) {
self.module_binding_map_key_value_types
.insert(binding_idx, (key, value));
}
pub(crate) fn map_key_value_for_local(
&self,
slot: u16,
) -> Option<&(ConcreteType, ConcreteType)> {
self.local_map_key_value_types.get(&slot)
}
pub(crate) fn map_key_value_for_module_binding(
&self,
binding_idx: u16,
) -> Option<&(ConcreteType, ConcreteType)> {
self.module_binding_map_key_value_types.get(&binding_idx)
}
pub(crate) fn map_key_value_for_node(
&self,
span: Span,
) -> Option<&(ConcreteType, ConcreteType)> {
self.map_key_value_types.get(&span)
}
pub(crate) fn try_track_hashmap_binding_from_annotation(
&mut self,
annotation: &TypeAnnotation,
slot: u16,
is_local: bool,
init_span: Option<Span>,
) -> bool {
if let Some((k, v)) = map_key_value_from_annotation(annotation) {
if is_local {
self.record_map_key_value_for_local(slot, k.clone(), v.clone());
} else {
self.record_map_key_value_for_module_binding(slot, k.clone(), v.clone());
}
if let Some(span) = init_span {
self.record_map_key_value_for_node(span, k, v);
}
true
} else {
false
}
}
pub(crate) fn resolve_receiver_map_key_value(
&self,
receiver: &Expr,
) -> Option<(ConcreteType, ConcreteType)> {
match receiver {
Expr::Identifier(name, _) => {
if let Some(slot) = self.resolve_local(name) {
if let Some(kv) = self.map_key_value_for_local(slot).cloned() {
return Some(kv);
}
}
if let Some(&binding_idx) = self.module_bindings.get(name) {
if let Some(kv) = self.map_key_value_for_module_binding(binding_idx).cloned()
{
return Some(kv);
}
}
if let Some(slot) = self.resolve_local(name) {
if let Some(info) = self.type_tracker.get_local_type(slot) {
if let Some(name) = info.type_name.as_deref() {
if let Some(kv) = parse_hashmap_kv_from_tracked_name(name) {
return Some(kv);
}
}
}
}
if let Some(&binding_idx) = self.module_bindings.get(name) {
if let Some(info) = self.type_tracker.get_binding_type(binding_idx) {
if let Some(name) = info.type_name.as_deref() {
if let Some(kv) = parse_hashmap_kv_from_tracked_name(name) {
return Some(kv);
}
}
}
}
None
}
_ => None,
}
}
pub(crate) fn record_array_element_type(&mut self, span: Span, element: ConcreteType) {
self.array_element_types.insert(span, element);
}
pub(crate) fn get_array_element_type(&self, span: Span) -> Option<&ConcreteType> {
self.array_element_types.get(&span)
}
pub(crate) fn is_typed_map_receiver(&self, receiver: &Expr) -> bool {
self.resolve_receiver_typed_map_kind(receiver).is_some()
}
pub(crate) fn resolve_receiver_typed_map_kind(
&self,
receiver: &Expr,
) -> Option<crate::compiler::v2_typed_map_emission::TypedMapKind> {
if let Expr::Identifier(name, _) = receiver {
if let Some(local_idx) = self.resolve_local(name) {
if let Some(&kind) = self.v2_typed_map_locals.get(&local_idx) {
return Some(kind);
}
}
if let Some(&binding_idx) = self.module_bindings.get(name) {
if let Some(&kind) = self.v2_typed_map_module_bindings.get(&binding_idx) {
return Some(kind);
}
}
return None;
}
if let Expr::MethodCall { receiver: inner, method, .. } = receiver {
if matches!(method.as_str(), "set" | "delete") {
if let Some(kind) = self.resolve_receiver_typed_map_kind(inner) {
return Some(kind);
}
}
}
if matches!(receiver, Expr::FunctionCall { .. } | Expr::MethodCall { .. }) {
if let Some(shape_value::v2::ConcreteType::HashMap(k, v)) =
crate::compiler::monomorphization::type_resolution::concrete_type_for_expr(
self, receiver,
)
{
return crate::compiler::v2_typed_map_emission::should_use_typed_map(&k, &v);
}
}
None
}
}
pub(crate) fn infer_hashmap_kv_from_context(
compiler: &BytecodeCompiler,
expr: &Expr,
) -> Option<(ConcreteType, ConcreteType)> {
let span = shape_ast::ast::Spanned::span(expr);
if let Some(kv) = compiler.map_key_value_for_node(span).cloned() {
return Some(kv);
}
if let Some(kind) = compiler.pending_variable_typed_map_kind {
return Some(typed_map_kind_to_concrete_kv(kind));
}
None
}
fn typed_map_kind_to_concrete_kv(
kind: crate::compiler::v2_typed_map_emission::TypedMapKind,
) -> (ConcreteType, ConcreteType) {
use crate::compiler::v2_typed_map_emission::TypedMapKind;
match kind {
TypedMapKind::StringF64 => (ConcreteType::String, ConcreteType::F64),
TypedMapKind::StringI64 => (ConcreteType::String, ConcreteType::I64),
TypedMapKind::StringPtr => (ConcreteType::String, ConcreteType::String),
TypedMapKind::I64F64 => (ConcreteType::I64, ConcreteType::F64),
TypedMapKind::I64I64 => (ConcreteType::I64, ConcreteType::I64),
TypedMapKind::I64Ptr => (ConcreteType::I64, ConcreteType::String),
}
}
pub(crate) fn parse_hashmap_kv_from_tracked_name(name: &str) -> Option<(ConcreteType, ConcreteType)> {
let trimmed = name.trim();
let inner = trimmed
.strip_prefix("HashMap<")
.or_else(|| trimmed.strip_prefix("Map<"))?
.strip_suffix('>')?;
let mut depth = 0i32;
let mut split_idx = None;
for (i, c) in inner.char_indices() {
match c {
'<' => depth += 1,
'>' => depth -= 1,
',' if depth == 0 => {
split_idx = Some(i);
break;
}
_ => {}
}
}
let idx = split_idx?;
let k_name = inner[..idx].trim();
let v_name = inner[idx + 1..].trim();
let k = scalar_name_to_concrete(k_name)?;
let v = scalar_name_to_concrete(v_name)?;
Some((k, v))
}
fn scalar_name_to_concrete(name: &str) -> Option<ConcreteType> {
match name {
"number" | "float" | "f64" | "Number" => Some(ConcreteType::F64),
"int" | "i64" | "integer" | "Int" => Some(ConcreteType::I64),
"i32" => Some(ConcreteType::I32),
"i16" => Some(ConcreteType::I16),
"i8" => Some(ConcreteType::I8),
"u64" => Some(ConcreteType::U64),
"u32" => Some(ConcreteType::U32),
"u16" => Some(ConcreteType::U16),
"u8" => Some(ConcreteType::U8),
"bool" | "boolean" | "Bool" => Some(ConcreteType::Bool),
"string" | "str" | "String" => Some(ConcreteType::String),
"decimal" | "Decimal" => Some(ConcreteType::Decimal),
"bigint" | "BigInt" => Some(ConcreteType::BigInt),
"DateTime" | "datetime" | "Time" => Some(ConcreteType::DateTime),
_ => None,
}
}
pub fn map_key_value_from_annotation(
annotation: &TypeAnnotation,
) -> Option<(ConcreteType, ConcreteType)> {
match annotation {
TypeAnnotation::Generic { name, args }
if (name == "HashMap" || name == "Map") && args.len() == 2 =>
{
let k = concrete_type_from_annotation(&args[0])?;
let v = concrete_type_from_annotation(&args[1])?;
Some((k, v))
}
TypeAnnotation::Generic { name, args } if name == "Option" && args.len() == 1 => {
map_key_value_from_annotation(&args[0])
}
_ => None,
}
}
pub fn concrete_type_from_annotation(annotation: &TypeAnnotation) -> Option<ConcreteType> {
match annotation {
TypeAnnotation::Basic(name) => match name.as_str() {
"number" | "float" | "f64" => Some(ConcreteType::F64),
"int" | "i64" | "integer" => Some(ConcreteType::I64),
"i32" => Some(ConcreteType::I32),
"i16" => Some(ConcreteType::I16),
"i8" => Some(ConcreteType::I8),
"u64" => Some(ConcreteType::U64),
"u32" => Some(ConcreteType::U32),
"u16" => Some(ConcreteType::U16),
"u8" => Some(ConcreteType::U8),
"bool" | "boolean" => Some(ConcreteType::Bool),
"string" | "str" => Some(ConcreteType::String),
"decimal" => Some(ConcreteType::Decimal),
"bigint" => Some(ConcreteType::BigInt),
"DateTime" | "datetime" | "Time" => Some(ConcreteType::DateTime),
"void" | "unit" => Some(ConcreteType::Void),
_ => None,
},
TypeAnnotation::Reference(path) => {
concrete_type_from_annotation(&TypeAnnotation::Basic(path.to_string()))
}
TypeAnnotation::Array(inner) => {
let elem = concrete_type_from_annotation(inner)?;
Some(ConcreteType::Array(Box::new(elem)))
}
TypeAnnotation::Generic { name, args } => match name.as_str() {
"Array" | "Vec" if args.len() == 1 => {
let elem = concrete_type_from_annotation(&args[0])?;
Some(ConcreteType::Array(Box::new(elem)))
}
"HashMap" | "Map" if args.len() == 2 => {
let k = concrete_type_from_annotation(&args[0])?;
let v = concrete_type_from_annotation(&args[1])?;
Some(ConcreteType::HashMap(Box::new(k), Box::new(v)))
}
"Option" if args.len() == 1 => {
let inner = concrete_type_from_annotation(&args[0])?;
Some(ConcreteType::Option(Box::new(inner)))
}
"Result" if args.len() == 2 => {
let ok = concrete_type_from_annotation(&args[0])?;
let err = concrete_type_from_annotation(&args[1])?;
Some(ConcreteType::Result(Box::new(ok), Box::new(err)))
}
_ => None,
},
TypeAnnotation::Function { .. } => Some(ConcreteType::Function(
shape_value::v2::concrete_type::FunctionTypeId(0),
)),
_ => None,
}
}
pub fn map_key_value_from_object_literal(
entries: &[ObjectEntry],
) -> Option<(ConcreteType, ConcreteType)> {
if entries.is_empty() {
return None;
}
let mut value_kind: Option<ConcreteType> = None;
for entry in entries {
let value_expr = match entry {
ObjectEntry::Field { value, .. } => value,
ObjectEntry::Spread(_) => return None,
};
let kind = literal_concrete_type(value_expr)?;
match &value_kind {
Some(prev) if *prev != kind => return None,
Some(_) => {}
None => value_kind = Some(kind),
}
}
let value_kind = value_kind?;
Some((ConcreteType::String, value_kind))
}
fn literal_concrete_type(expr: &Expr) -> Option<ConcreteType> {
match expr {
Expr::Literal(Literal::Number(_), _) => Some(ConcreteType::F64),
Expr::Literal(Literal::Int(_), _) => Some(ConcreteType::I64),
Expr::Literal(Literal::Bool(_), _) => Some(ConcreteType::Bool),
Expr::Literal(Literal::String(_), _) => Some(ConcreteType::String),
Expr::Literal(Literal::Decimal(_), _) => Some(ConcreteType::Decimal),
Expr::Literal(Literal::TypedInt(_, w), _) => Some(typed_int_width_to_concrete(*w)),
_ => None,
}
}
fn typed_int_width_to_concrete(w: shape_ast::IntWidth) -> ConcreteType {
use shape_ast::IntWidth;
match w {
IntWidth::I8 => ConcreteType::I8,
IntWidth::U8 => ConcreteType::U8,
IntWidth::I16 => ConcreteType::I16,
IntWidth::U16 => ConcreteType::U16,
IntWidth::I32 => ConcreteType::I32,
IntWidth::U32 => ConcreteType::U32,
IntWidth::U64 => ConcreteType::U64,
}
}
#[cfg(test)]
mod tests {
use super::*;
use shape_ast::ast::Span;
use shape_ast::ast::type_path::TypePath;
fn span() -> Span {
Span::default()
}
fn ann_basic(name: &str) -> TypeAnnotation {
TypeAnnotation::Basic(name.to_string())
}
fn hashmap_ann(k: TypeAnnotation, v: TypeAnnotation) -> TypeAnnotation {
TypeAnnotation::Generic {
name: TypePath::simple("HashMap"),
args: vec![k, v],
}
}
fn obj_field(key: &str, value: Expr) -> ObjectEntry {
ObjectEntry::Field {
key: key.to_string(),
value,
type_annotation: None,
}
}
#[test]
fn test_hashmap_string_int() {
let ann = hashmap_ann(ann_basic("string"), ann_basic("int"));
let (k, v) = map_key_value_from_annotation(&ann).expect("HashMap<string, int>");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::I64);
}
#[test]
fn test_hashmap_string_number() {
let ann = hashmap_ann(ann_basic("string"), ann_basic("number"));
let (k, v) = map_key_value_from_annotation(&ann).expect("HashMap<string, number>");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::F64);
}
#[test]
fn test_hashmap_int_bool() {
let ann = hashmap_ann(ann_basic("int"), ann_basic("bool"));
let (k, v) = map_key_value_from_annotation(&ann).expect("HashMap<int, bool>");
assert_eq!(k, ConcreteType::I64);
assert_eq!(v, ConcreteType::Bool);
}
#[test]
fn test_hashmap_alias_map() {
let ann = TypeAnnotation::Generic {
name: TypePath::simple("Map"),
args: vec![ann_basic("string"), ann_basic("number")],
};
let (k, v) = map_key_value_from_annotation(&ann).expect("Map<string, number>");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::F64);
}
#[test]
fn test_hashmap_nested_array_value() {
let inner = TypeAnnotation::Generic {
name: TypePath::simple("Array"),
args: vec![ann_basic("int")],
};
let ann = hashmap_ann(ann_basic("string"), inner);
let (k, v) = map_key_value_from_annotation(&ann).expect("HashMap<string, Array<int>>");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::Array(Box::new(ConcreteType::I64)));
}
#[test]
fn test_hashmap_nested_hashmap_value() {
let inner = hashmap_ann(ann_basic("string"), ann_basic("int"));
let ann = hashmap_ann(ann_basic("string"), inner);
let (k, v) =
map_key_value_from_annotation(&ann).expect("HashMap<string, HashMap<string, int>>");
assert_eq!(k, ConcreteType::String);
assert_eq!(
v,
ConcreteType::HashMap(Box::new(ConcreteType::String), Box::new(ConcreteType::I64),)
);
}
#[test]
fn test_non_hashmap_returns_none() {
let ann = TypeAnnotation::Generic {
name: TypePath::simple("Array"),
args: vec![ann_basic("int")],
};
assert_eq!(map_key_value_from_annotation(&ann), None);
}
#[test]
fn test_hashmap_wrong_arity_returns_none() {
let ann = TypeAnnotation::Generic {
name: TypePath::simple("HashMap"),
args: vec![ann_basic("string")],
};
assert_eq!(map_key_value_from_annotation(&ann), None);
}
#[test]
fn test_basic_string_returns_none() {
assert_eq!(map_key_value_from_annotation(&ann_basic("string")), None);
}
#[test]
fn test_unresolved_user_type_returns_none() {
let ann = hashmap_ann(ann_basic("string"), ann_basic("MyStruct"));
assert_eq!(map_key_value_from_annotation(&ann), None);
}
#[test]
fn test_optional_hashmap_unwraps() {
let ann = TypeAnnotation::Generic {
name: TypePath::simple("Option"),
args: vec![hashmap_ann(ann_basic("string"), ann_basic("int"))],
};
let (k, v) = map_key_value_from_annotation(&ann).expect("Option<HashMap<string, int>>");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::I64);
}
#[test]
fn test_object_literal_string_int_values() {
let entries = vec![
obj_field("a", Expr::Literal(Literal::Int(1), span())),
obj_field("b", Expr::Literal(Literal::Int(2), span())),
];
let (k, v) = map_key_value_from_object_literal(&entries).expect("homogeneous int values");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::I64);
}
#[test]
fn test_object_literal_string_number_values() {
let entries = vec![
obj_field("x", Expr::Literal(Literal::Number(1.5), span())),
obj_field("y", Expr::Literal(Literal::Number(2.5), span())),
];
let (k, v) =
map_key_value_from_object_literal(&entries).expect("homogeneous number values");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::F64);
}
#[test]
fn test_object_literal_string_string_values() {
let entries = vec![
obj_field("a", Expr::Literal(Literal::String("foo".into()), span())),
obj_field("b", Expr::Literal(Literal::String("bar".into()), span())),
];
let (k, v) =
map_key_value_from_object_literal(&entries).expect("homogeneous string values");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::String);
}
#[test]
fn test_object_literal_string_bool_values() {
let entries = vec![
obj_field("a", Expr::Literal(Literal::Bool(true), span())),
obj_field("b", Expr::Literal(Literal::Bool(false), span())),
];
let (k, v) = map_key_value_from_object_literal(&entries).expect("homogeneous bool values");
assert_eq!(k, ConcreteType::String);
assert_eq!(v, ConcreteType::Bool);
}
#[test]
fn test_object_literal_heterogeneous_values_returns_none() {
let entries = vec![
obj_field("a", Expr::Literal(Literal::Int(1), span())),
obj_field("b", Expr::Literal(Literal::String("two".into()), span())),
];
assert_eq!(map_key_value_from_object_literal(&entries), None);
}
#[test]
fn test_object_literal_int_and_number_returns_none() {
let entries = vec![
obj_field("a", Expr::Literal(Literal::Int(1), span())),
obj_field("b", Expr::Literal(Literal::Number(2.0), span())),
];
assert_eq!(map_key_value_from_object_literal(&entries), None);
}
#[test]
fn test_object_literal_empty_returns_none() {
assert_eq!(map_key_value_from_object_literal(&[]), None);
}
#[test]
fn test_object_literal_with_non_literal_returns_none() {
let entries = vec![
obj_field("a", Expr::Literal(Literal::Int(1), span())),
obj_field("b", Expr::Identifier("some_var".to_string(), span())),
];
assert_eq!(map_key_value_from_object_literal(&entries), None);
}
#[test]
fn test_object_literal_with_spread_returns_none() {
let entries = vec![
obj_field("a", Expr::Literal(Literal::Int(1), span())),
ObjectEntry::Spread(Expr::Identifier("other".to_string(), span())),
];
assert_eq!(map_key_value_from_object_literal(&entries), None);
}
#[test]
fn test_concrete_type_primitives() {
assert_eq!(
concrete_type_from_annotation(&ann_basic("int")),
Some(ConcreteType::I64)
);
assert_eq!(
concrete_type_from_annotation(&ann_basic("number")),
Some(ConcreteType::F64)
);
assert_eq!(
concrete_type_from_annotation(&ann_basic("bool")),
Some(ConcreteType::Bool)
);
assert_eq!(
concrete_type_from_annotation(&ann_basic("string")),
Some(ConcreteType::String)
);
assert_eq!(
concrete_type_from_annotation(&ann_basic("decimal")),
Some(ConcreteType::Decimal)
);
assert_eq!(
concrete_type_from_annotation(&ann_basic("u8")),
Some(ConcreteType::U8)
);
}
#[test]
fn test_concrete_type_array_of_int() {
let ann = TypeAnnotation::Array(Box::new(ann_basic("int")));
assert_eq!(
concrete_type_from_annotation(&ann),
Some(ConcreteType::Array(Box::new(ConcreteType::I64)))
);
}
#[test]
fn test_concrete_type_generic_array_of_number() {
let ann = TypeAnnotation::Generic {
name: TypePath::simple("Array"),
args: vec![ann_basic("number")],
};
assert_eq!(
concrete_type_from_annotation(&ann),
Some(ConcreteType::Array(Box::new(ConcreteType::F64)))
);
}
#[test]
fn test_concrete_type_unknown_returns_none() {
assert_eq!(concrete_type_from_annotation(&ann_basic("MyStruct")), None);
}
fn fresh_compiler() -> BytecodeCompiler {
BytecodeCompiler::new()
}
#[test]
fn test_record_and_lookup_local_map_kv() {
let mut compiler = fresh_compiler();
compiler.record_map_key_value_for_local(7, ConcreteType::String, ConcreteType::I64);
let (k, v) = compiler.map_key_value_for_local(7).expect("recorded entry");
assert_eq!(*k, ConcreteType::String);
assert_eq!(*v, ConcreteType::I64);
assert_eq!(compiler.map_key_value_for_local(8), None);
}
#[test]
fn test_record_and_lookup_module_binding_map_kv() {
let mut compiler = fresh_compiler();
compiler.record_map_key_value_for_module_binding(3, ConcreteType::I64, ConcreteType::F64);
let (k, v) = compiler
.map_key_value_for_module_binding(3)
.expect("recorded entry");
assert_eq!(*k, ConcreteType::I64);
assert_eq!(*v, ConcreteType::F64);
}
#[test]
fn test_record_and_lookup_node_map_kv() {
let mut compiler = fresh_compiler();
let s = Span::new(10, 20);
compiler.record_map_key_value_for_node(s, ConcreteType::String, ConcreteType::Bool);
let (k, v) = compiler.map_key_value_for_node(s).expect("recorded entry");
assert_eq!(*k, ConcreteType::String);
assert_eq!(*v, ConcreteType::Bool);
}
#[test]
fn test_try_track_hashmap_binding_from_annotation_local() {
let mut compiler = fresh_compiler();
let ann = hashmap_ann(ann_basic("string"), ann_basic("int"));
let init_span = Span::new(100, 110);
let tracked =
compiler.try_track_hashmap_binding_from_annotation(&ann, 5, true, Some(init_span));
assert!(tracked, "should have tracked HashMap binding");
let (k, v) = compiler.map_key_value_for_local(5).expect("local recorded");
assert_eq!(*k, ConcreteType::String);
assert_eq!(*v, ConcreteType::I64);
let (k2, v2) = compiler
.map_key_value_for_node(init_span)
.expect("node recorded");
assert_eq!(*k2, ConcreteType::String);
assert_eq!(*v2, ConcreteType::I64);
}
#[test]
fn test_try_track_hashmap_binding_from_annotation_module() {
let mut compiler = fresh_compiler();
let ann = hashmap_ann(ann_basic("int"), ann_basic("number"));
let tracked = compiler.try_track_hashmap_binding_from_annotation(&ann, 2, false, None);
assert!(tracked);
let (k, v) = compiler
.map_key_value_for_module_binding(2)
.expect("module binding recorded");
assert_eq!(*k, ConcreteType::I64);
assert_eq!(*v, ConcreteType::F64);
}
#[test]
fn test_try_track_hashmap_binding_from_non_hashmap_annotation_returns_false() {
let mut compiler = fresh_compiler();
let ann = ann_basic("int");
let tracked = compiler.try_track_hashmap_binding_from_annotation(&ann, 1, true, None);
assert!(!tracked);
assert_eq!(compiler.map_key_value_for_local(1), None);
}
#[test]
fn test_resolve_receiver_map_key_value_from_non_identifier_returns_none() {
let compiler = fresh_compiler();
let receiver = Expr::Literal(Literal::Int(0), span());
assert_eq!(compiler.resolve_receiver_map_key_value(&receiver), None);
}
#[test]
fn test_phase22_keys_records_array_of_k() {
let mut compiler = fresh_compiler();
let call_span = Span::new(100, 110);
let k = ConcreteType::String;
let v = ConcreteType::I64;
compiler.record_array_element_type(call_span, k.clone());
compiler.record_map_key_value_for_node(call_span, k.clone(), v);
let elem = compiler
.get_array_element_type(call_span)
.expect("array element type recorded");
assert_eq!(*elem, ConcreteType::String);
}
#[test]
fn test_phase22_values_records_array_of_v_int() {
let mut compiler = fresh_compiler();
let call_span = Span::new(200, 210);
let k = ConcreteType::String;
let v = ConcreteType::I64;
compiler.record_array_element_type(call_span, v.clone());
compiler.record_map_key_value_for_node(call_span, k, v);
let elem = compiler
.get_array_element_type(call_span)
.expect("array element type recorded");
assert_eq!(*elem, ConcreteType::I64);
}
#[test]
fn test_phase22_get_records_kv_metadata_no_array() {
let mut compiler = fresh_compiler();
let call_span = Span::new(300, 310);
let k = ConcreteType::String;
let v = ConcreteType::F64;
compiler.record_map_key_value_for_node(call_span, k.clone(), v.clone());
assert_eq!(compiler.get_array_element_type(call_span), None);
let (rk, rv) = compiler
.map_key_value_for_node(call_span)
.expect("kv metadata recorded");
assert_eq!(*rk, ConcreteType::String);
assert_eq!(*rv, ConcreteType::F64);
}
#[test]
fn test_phase22_entries_records_array_of_tuple_kv() {
let mut compiler = fresh_compiler();
let call_span = Span::new(400, 410);
let k = ConcreteType::String;
let v = ConcreteType::I64;
let pair = ConcreteType::Tuple(vec![k.clone(), v.clone()]);
compiler.record_array_element_type(call_span, pair.clone());
compiler.record_map_key_value_for_node(call_span, k, v);
let elem = compiler
.get_array_element_type(call_span)
.expect("array element type recorded");
assert_eq!(
*elem,
ConcreteType::Tuple(vec![ConcreteType::String, ConcreteType::I64])
);
}
#[test]
fn test_phase22_set_preserves_kv_metadata() {
let mut compiler = fresh_compiler();
let call_span = Span::new(500, 510);
let k = ConcreteType::I64;
let v = ConcreteType::String;
compiler.record_map_key_value_for_node(call_span, k.clone(), v.clone());
let (rk, rv) = compiler
.map_key_value_for_node(call_span)
.expect("kv metadata recorded after set");
assert_eq!(*rk, ConcreteType::I64);
assert_eq!(*rv, ConcreteType::String);
assert_eq!(compiler.get_array_element_type(call_span), None);
}
#[test]
fn test_phase22_split_records_array_of_string() {
let mut compiler = fresh_compiler();
let call_span = Span::new(600, 610);
compiler.record_array_element_type(call_span, ConcreteType::String);
let elem = compiler
.get_array_element_type(call_span)
.expect("array element type recorded");
assert_eq!(*elem, ConcreteType::String);
}
#[test]
fn ws6b_typed_map_local_in_function_set_get() {
assert_eq!(
crate::test_utils::eval_typed_i64(
"fn run() -> int {\n\
let mut m: HashMap<string,int> = HashMap()\n\
m.set(\"k\", 1)\n\
m.get(\"k\")\n\
}\n\
run()"
),
1
);
}
#[test]
fn ws6b_typed_map_generic_call_result_get() {
assert_eq!(
crate::test_utils::eval_typed_i64(
"fn id<T>(x: T) -> T { x }\n\
let mut m: HashMap<string,int> = HashMap()\n\
m.set(\"k\", 1)\n\
id(m).get(\"k\")"
),
1
);
}
#[test]
fn ws6b_typed_map_nongeneric_call_result_get() {
assert_eq!(
crate::test_utils::eval_typed_i64(
"fn mk() -> HashMap<string,int> {\n\
let mut m: HashMap<string,int> = HashMap()\n\
m.set(\"k\", 1)\n\
m\n\
}\n\
mk().get(\"k\")"
),
1
);
}
#[test]
fn ws6b_typed_map_call_result_fluent_set_chain() {
assert_eq!(
crate::test_utils::eval_typed_i64(
"fn id<T>(x: T) -> T { x }\n\
let mut m: HashMap<string,int> = HashMap()\n\
id(m).set(\"a\", 5).get(\"a\")"
),
5
);
}
}