diff --git a/ndc_analyser/src/analyser/declarations.rs b/ndc_analyser/src/analyser/declarations.rs index 4a83550b..5f61d35e 100644 --- a/ndc_analyser/src/analyser/declarations.rs +++ b/ndc_analyser/src/analyser/declarations.rs @@ -50,6 +50,7 @@ impl Analyser { } let first_use = self.uses.len(); let first_literal = self.number_literals.len(); + let first_check = self.checks.len(); let (annotations, iterates): (Vec<_>, Vec<_>) = parameters .iter() .map(|param| self.lower_parameter_annotation(param.annotation.as_ref())) @@ -200,7 +201,12 @@ impl Analyser { self.vars.leave_level(); self.declaring = None; self.declared_in_batch.insert(symbol); - self.generalize(symbol, &signature, id, first_use, first_literal) + self.generalize( + symbol, + &signature, + id, + [first_use, first_literal, first_check], + ) } _ => signature, }; diff --git a/ndc_analyser/src/analyser/generics.rs b/ndc_analyser/src/analyser/generics.rs index 3dc169ff..21a49095 100644 --- a/ndc_analyser/src/analyser/generics.rs +++ b/ndc_analyser/src/analyser/generics.rs @@ -38,8 +38,7 @@ impl Analyser { symbol: SymbolId, signature: &StaticType, declaration: NodeId, - first_use: usize, - first_literal: usize, + [first_use, first_literal, first_check]: [usize; 3], ) -> StaticType { self.solve_waiting(); // A literal in the body that its type parameters don't reach has @@ -191,15 +190,30 @@ impl Analyser { self.pick_for_any_arguments(call); } } - // An instance is for the types of the type parameters the - // constraints and uses depend on. + // A value of type `Any` that the body passes where a type parameter + // is expected, as `a` in `fn f(x) { let a: Any = 5; [x].push(a) }`, + // is checked against what each call makes it. + let checks = self.checks[first_check..] + .iter() + .filter(|(instance, ..)| instance.is_none()) + .map(|(_, node, typ, expected)| { + (*node, self.vars.resolve(typ), self.vars.resolve(expected)) + }) + .filter(|(_, typ, expected)| { + *typ == StaticType::Any + && expected.contains(&|part| matches!(part, StaticType::Generic(_))) + }) + .collect::>(); + // An instance is for the types of the type parameters that what it + // compiles differently depends on: the constraints and uses it + // picks for, the literals typed by a type parameter, and the checks. let mut key: Vec> = Vec::new(); - // The literals typed by a type parameter compile to what it is. for typ in scheme .iter() .flat_map(|waiting| waiting.constraint.inputs()) .chain(uses.iter().flat_map(|generic_use| &generic_use.key)) .chain(literal_nodes.iter().map(|(_, typ)| typ)) + .chain(checks.iter().map(|(_, _, expected)| expected)) { for name in typ.type_parameters() { if !key.contains(&name) { @@ -217,6 +231,7 @@ impl Analyser { recursive_calls: std::mem::take(&mut self.recursive_calls).into(), literals: literals.into(), literal_nodes: literal_nodes.into(), + checks: checks.into(), }, ); self.vars.resolve(signature) diff --git a/ndc_analyser/src/analyser/instances.rs b/ndc_analyser/src/analyser/instances.rs index 3f97a787..ffc46f32 100644 --- a/ndc_analyser/src/analyser/instances.rs +++ b/ndc_analyser/src/analyser/instances.rs @@ -43,6 +43,10 @@ pub(super) struct Scheme { /// parameters, with that type, which an instance records its own types /// for. pub(super) literal_nodes: Rc<[(NodeId, StaticType)]>, + /// The values its body checks as the program runs against a type that + /// has its type parameters in it, with their types and that type, which + /// an instance checks against what its types make it. + pub(super) checks: Rc<[(NodeId, StaticType, StaticType)]>, } /// A call of a generic function, or its name used as a value, which needs @@ -300,6 +304,11 @@ impl Analyser { self.wait_as(copy); } } + for (node, typ, expected) in scheme.checks.iter() { + let typ = self.vars.instantiate_with(typ, &mut made); + let expected = self.vars.instantiate_with(expected, &mut made); + self.expect_if_any(*node, typ, expected); + } for &call in scheme.recursive_calls.iter() { self.pick_tables(Some(instance)).insert( CallSite::Call(call), diff --git a/tests/functional/programs/005_functions/096_an_any_value_passed_for_a_type_parameter_is_checked.ndc b/tests/functional/programs/005_functions/096_an_any_value_passed_for_a_type_parameter_is_checked.ndc new file mode 100644 index 00000000..570c4a52 --- /dev/null +++ b/tests/functional/programs/005_functions/096_an_any_value_passed_for_a_type_parameter_is_checked.ndc @@ -0,0 +1,12 @@ +// A value of type `Any` passed where a generic function's type parameter is +// expected is checked against what each call makes it: `with_five("s")` +// pushes the Int 5 into a list of Strings. +// expect-error: expected String but found Int +fn with_five(x) { + let five: Any = 5; + let xs = [x]; + xs.push(five); + xs +} +print(with_five(1)); +print(with_five("s"));