diff --git a/src/check/constrain/constraint/builder.rs b/src/check/constrain/constraint/builder.rs index 99f1ef3c..0549c0b3 100644 --- a/src/check/constrain/constraint/builder.rs +++ b/src/check/constrain/constraint/builder.rs @@ -1,50 +1,51 @@ -use std::cmp::max; use std::collections::HashMap; -use crate::check::constrain::constraint::Constraint; +use itertools::enumerate; + +use crate::check::constrain::constraint::{Constraint, MapExp}; use crate::check::constrain::constraint::expected::Expected; use crate::check::constrain::constraint::iterator::Constraints; +use crate::check::constrain::generate::env::Environment; use crate::common::delimit::comma_delm; +use crate::common::position::Position; pub type VarMapping = HashMap; pub fn format_var_map(var: &str, offset: &usize) -> String { - if *offset == 0usize { + if *offset == 0_usize { String::from(var) } else { format!("{var}@{offset}") } } -/// Constraint Builder. +type ConstraintLvls = Vec<(Constraint, usize)>; + +/// The constraint builder allows us to build sets of constraints. /// -/// Allows us to build sets of constraints. /// This allows us to constrain different parts of the program which may rely on /// the same logic, without interfering with each other. E.g. different /// functions within the same class. /// -/// The level indicates how deep we are. A level of 0 indicates that we are at -/// the top-level of a script. -/// /// We use sets to type check all possible execution paths. /// We can have multiple sets open at a time. -/// When a constraint is added, we add it to each open path. #[derive(Debug)] pub struct ConstrBuilder { - finished: Vec>, - constraints: Vec>, + constraints: Vec<(Position, String, ConstraintLvls)>, + branch_point: usize, + joined: bool, pub var_mapping: VarMapping, } impl ConstrBuilder { + /// Create constraint builder with a single set present. pub fn new() -> ConstrBuilder { - trace!("Created set at level {}", 0); - ConstrBuilder { finished: vec![], constraints: vec![vec![]], var_mapping: HashMap::new() } + let var_mapping = VarMapping::new(); + let (pos, msg) = (Position::default(), String::from("Script")); + ConstrBuilder { branch_point: 0, joined: false, constraints: vec![(pos, msg, vec![])], var_mapping } } - pub fn is_top_level(&self) -> bool { self.constraints.len() == 1 } - /// Insert variable for mapping in current constraint set. /// /// This prevents shadowed variables from contaminating previous constraints. @@ -54,62 +55,235 @@ impl ConstrBuilder { pub fn insert_var(&mut self, var: &str) { let offset = self.var_mapping.get(var).map_or(0, |o| o + 1); self.var_mapping.insert(String::from(var), offset); - } - /// Create new set, and create marker so that we know what set to exit to upon exit. - /// - /// Output may also be ignored. - /// Useful if we don't want to close the set locally but leave open. - pub fn new_set(&mut self) -> usize { - let inherited_constraints = self.constraints.last().expect("Can never be empty"); - self.constraints.push(inherited_constraints.clone()); + let mapped_var = format_var_map(var, self.var_mapping.get(var).unwrap()); + trace!("Inserted {var} in constraint builder: {var} => {mapped_var}"); + } - trace!("Created set at level {}", self.constraints.len() - 1); - self.constraints.len() + /// Set new branch point. + pub fn branch_point(&mut self) { + trace!("Branch point created at level {}", self.constraints.len() - 1); + self.branch_point += 1; + self.joined = false; } - /// Return to specified level given. + /// Create new set starting at stated level. /// - /// - Error if already top-level. - /// - Error if level greater than ceiling, as we cannot exit non-existent sets. - pub fn exit_set_to(&mut self, level: usize) { - let msg_exit = format!("Exit set to level {}", level - 1); - - let level = max(1, level); - if level == 0 { - panic!("Cannot exit top-level set"); - } else if level > self.constraints.len() { - panic!("Exiting constraint set which doesn't exist\nlevel: {}, constraints: {}, finished: {}", - level, self.constraints.len(), self.finished.len()); - } + /// We use this if we want to add to a new set without adding to a certain set of previous ones. + /// Typically in match arms or if arms, where we want branches to be disjoint. + /// At the same time, we want all branches to inherit from an older set. + /// When inheriting, we also discard any constraints added while in a level we wish to skip. + pub fn branch(&mut self, msg: &str, pos: Position) { + trace!("Branching from level {}", self.branch_point - 1); + let inherited_constraints: ConstraintLvls = if self.joined { + self.constraints.last().expect("Is never empty").2.to_vec() + } else { + self.constraints.last().expect("Is never empty").2 + .iter().filter(|(_, lvl)| *lvl < self.branch_point).cloned().collect() + }; - for i in (level - 1..self.constraints.len()).rev() { - // Equivalent to pop, but remove has better panic message for debugging - self.finished.push(self.constraints.remove(i)) - } + self.constraints.push((pos, String::from(msg), inherited_constraints)); + } - trace!("{msg_exit}: {} active sets, {} complete sets", self.constraints.len(), self.finished.len()); + /// Reset all branches so that they are again all added to. + pub fn reset_branches(&mut self) { + trace!("Reset branches: Now adding to {} branches in parallel", self.constraints.len()); + self.branch_point = self.constraints.len() - 1; + self.joined = true; } /// Add new constraint to constraint builder with a message. - pub fn add(&mut self, msg: &str, parent: &Expected, child: &Expected) { - self.add_constr(&Constraint::new(msg, parent, child)); + /// + /// See [Self::add_constr] for mode details. + pub fn add(&mut self, msg: &str, parent: &Expected, child: &Expected, env: &Environment) { + self.add_constr_map(&Constraint::new(msg, parent, child), &env.var_mapping, false); + } + + /// Add new constraint and specify whether one wants the constraint builder to perform any + /// internal mapping. + /// + /// Given environment used for variable substitution. + /// This takes precedence over the global variable mapping. + /// + /// Useful if one want to have greater control over the order over how variables are mapped + /// within the [Expected]. + pub fn add_constr(&mut self, constraint: &Constraint, env: &Environment) { + self.add_constr_map(constraint, &env.var_mapping, false) } - /// Add constraint to currently all op sets. - /// The open sets are the sets at levels between the self.level and active ceiling. - pub fn add_constr(&mut self, constraint: &Constraint) { - for constraints in &mut self.constraints { - constraints.push(constraint.clone()); + /// Add new constraint and specify whether one wants the constraint builder to perform any + /// internal mapping. + /// + /// Useful if one want to have greater control over the order over how variables are mapped + /// within the [Expected]. + pub fn add_constr_map(&mut self, constraint: &Constraint, var_map: &VarMapping, ignore_map: bool) { + let (mut lvls, last_branch) = (vec![], self.constraints.len() - 1); + let constraint = if ignore_map { + constraint.clone() + } else { + constraint.map_exp(var_map, &self.var_mapping) + }; + + if self.joined { + for (i, (_, _, constraints)) in enumerate(&mut self.constraints) { + constraints.push((constraint.clone(), self.branch_point)); + lvls.push(i); + } + } else { + // only push to last branch + self.constraints[last_branch].2.push((constraint.clone(), self.branch_point)); + lvls.push(last_branch); } - let lvls = comma_delm(0..self.constraints.len()); + let lvls = comma_delm(lvls); trace!("Constr[{}]: {} == {}, {}: {}", lvls, constraint.left.pos, constraint.right.pos, constraint.msg, constraint); } pub fn all_constr(self) -> Vec { - let (mut finished, mut constraints) = (self.finished, self.constraints); - finished.append(&mut constraints); - finished.iter().map(Constraints::from).collect() + let constraints: Vec<(Position, String, Vec)> = self.constraints.into_iter() + .map(|(pos, msg, constraints)| { + (pos, msg, constraints.iter().map(|(c, _)| c.clone()).collect()) + }) + .collect(); + + constraints.into_iter().map(Constraints::from).collect() + } +} + +#[cfg(test)] +mod tests { + use crate::check::constrain::constraint::builder::ConstrBuilder; + use crate::check::constrain::constraint::Constraint; + use crate::check::constrain::constraint::expected::Expected; + use crate::check::constrain::generate::env::Environment; + use crate::common::position::Position; + + macro_rules! constr { + ($msg:expr) => {{ + Constraint::new(format!("{}", $msg).as_str(), + &Expected::any(Position::default()), + &Expected::any(Position::default())) + }} + } + + macro_rules! assert_eq_constr { + ($left:expr, $right:expr) => {{ + let left = $left.iter().map(|c| c.msg.clone()).collect::>(); + let right = $right.iter().map(|c| c.msg.clone()).collect::>(); + assert_eq!(left, right); + }} + } + + #[test] + fn all_constr_present() { + let mut builder = ConstrBuilder::new(); + let (c1, c2, c3) = (constr!(1), constr!(2), constr!(3)); + + builder.add_constr(&c1, &Environment::default()); + builder.add_constr(&c2, &Environment::default()); + builder.add_constr(&c3, &Environment::default()); + + let all_constr = builder.all_constr(); + assert_eq!(all_constr.len(), 1); + assert_eq_constr!(all_constr[0].constraints, vec![c1, c2, c3]) + } + + #[test] + fn disjoint_set_if() { + let mut builder = ConstrBuilder::new(); + let (c1, c2, c3, c4) = (constr!(1), constr!(2), constr!(3), constr!(4)); + + builder.add_constr(&c1, &Environment::default()); // anything before if branches (including cond) + + builder.branch_point(); + builder.add_constr(&c2, &Environment::default()); // then branch of if + + builder.branch("", Position::default()); + builder.add_constr(&c3, &Environment::default()); // else branch of if + + builder.reset_branches(); + builder.add_constr(&c4, &Environment::default()); // anything after if + + let all_constr = builder.all_constr(); + assert_eq!(all_constr.len(), 2); + + assert_eq_constr!(all_constr[0].constraints, [&c1, &c2, &c4]); + assert_eq_constr!(all_constr[1].constraints, [&c1, &c3, &c4]); + } + + #[test] + fn disjoint_set_match() { + let mut builder = ConstrBuilder::new(); + let (c1, c2, c3, c4, c5) = (constr!(1), constr!(2), constr!(3), constr!(4), constr!(5)); + + builder.add_constr(&c1, &Environment::default()); // anything before match branches (including expr) + + builder.branch_point(); + builder.add_constr(&c2, &Environment::default()); // first branch + + builder.branch("", Position::default()); + builder.add_constr(&c3, &Environment::default()); // second branch + + builder.branch("", Position::default()); + builder.add_constr(&c4, &Environment::default()); // third branch + + builder.reset_branches(); + builder.add_constr(&c5, &Environment::default()); // anything after match + + let all_constr = builder.all_constr(); + assert_eq!(all_constr.len(), 3); + + assert_eq_constr!(all_constr[0].constraints, [&c1, &c2, &c5]); + assert_eq_constr!(all_constr[1].constraints, [&c1, &c3, &c5]); + assert_eq_constr!(all_constr[2].constraints, [&c1, &c4, &c5]); + } + + #[test] + fn disjoint_set_nested_match() { + let mut builder = ConstrBuilder::new(); + let (c1, c2, _, c4, c5) = (constr!(1), constr!(2), constr!(3), constr!(4), constr!(5)); + let (c31, c32, c33, c34, c35) = (constr!(31), constr!(32), constr!(33), constr!(34), constr!(35)); + + builder.add_constr(&c1, &Environment::default()); // anything before match branches (including expr) + + builder.branch_point(); + builder.add_constr(&c2, &Environment::default()); // first branch + + builder.branch("", Position::default()); + { // second branch + builder.branch_point(); + builder.add_constr(&c31, &Environment::default()); + + builder.branch("", Position::default()); + builder.add_constr(&c32, &Environment::default()); + + builder.branch("", Position::default()); + builder.add_constr(&c33, &Environment::default()); + + builder.branch("", Position::default()); + builder.add_constr(&c34, &Environment::default()); + + builder.branch("", Position::default()); + builder.add_constr(&c35, &Environment::default()); + } + + builder.branch("", Position::default()); + builder.add_constr(&c4, &Environment::default()); // third branch + + builder.reset_branches(); + builder.add_constr(&c5, &Environment::default()); // anything after match + + let all_constr = builder.all_constr(); + assert_eq!(all_constr.len(), 7); + + assert_eq_constr!(all_constr[0].constraints, [&c1, &c2, &c5]); + + assert_eq_constr!(all_constr[1].constraints, [&c1, &c31, &c5]); + assert_eq_constr!(all_constr[2].constraints, [&c1, &c32, &c5]); + assert_eq_constr!(all_constr[3].constraints, [&c1, &c33, &c5]); + assert_eq_constr!(all_constr[4].constraints, [&c1, &c34, &c5]); + assert_eq_constr!(all_constr[5].constraints, [&c1, &c35, &c5]); + + assert_eq_constr!(all_constr[6].constraints, [&c1, &c4, &c5]); } } diff --git a/src/check/constrain/constraint/expected.rs b/src/check/constrain/constraint/expected.rs index e58a7a02..f3ec8ba5 100644 --- a/src/check/constrain/constraint/expected.rs +++ b/src/check/constrain/constraint/expected.rs @@ -1,4 +1,3 @@ -use std::convert::TryFrom; use std::fmt; use std::fmt::{Display, Error, Formatter}; use std::hash::Hash; @@ -8,10 +7,10 @@ use itertools::{EitherOrBoth, Itertools}; use crate::check::constrain::constraint::builder::{format_var_map, VarMapping}; use crate::check::constrain::constraint::expected::Expect::*; +use crate::check::constrain::constraint::MapExp; use crate::check::context::clss::NONE; use crate::check::name::{Any, Name, Nullable}; use crate::check::name::string_name::StringName; -use crate::check::result::{TypeErr, TypeResult}; use crate::common::delimit::comma_delm; use crate::common::position::Position; use crate::common::result::an_or_a; @@ -24,6 +23,51 @@ pub struct Expected { an_or_a: bool, } +impl MapExp for Expected { + fn map_exp(&self, var_mapping: &VarMapping, global_var_mapping: &VarMapping) -> Self { + Expected::new(self.pos, &match &self.expect { + Expression { ast } => { + let ast = match &ast.node { + Node::Block { statements } if statements.is_empty() => ast.clone(), + Node::Block { statements } => statements.last().cloned().expect("unreachable"), + _ => ast.clone(), + }; + + Expression { + ast: ast.map(&|node: &Node| if let Node::Id { lit } = node { + let offset = if let Some(offset) = var_mapping.get(lit) { + *offset + } else if let Some(offset) = global_var_mapping.get(lit) { + *offset + } else { + 0_usize + }; + + Node::Id { lit: format_var_map(lit, &offset) } + } else { + node.clone() + }) + } + } + Collection { ty } => Collection { + ty: Box::from(ty.map_exp(var_mapping, global_var_mapping)) + }, + Tuple { elements } => Tuple { + elements: elements.iter().map(|e| e.map_exp(var_mapping, global_var_mapping)).collect() + }, + Function { name, args } => Function { + name: name.clone(), + args: args.iter().map(|a| a.map_exp(var_mapping, global_var_mapping)).collect(), + }, + Access { entity, name } => Access { + entity: Box::from(entity.map_exp(var_mapping, global_var_mapping)), + name: Box::from(name.map_exp(var_mapping, global_var_mapping)), + }, + other => other.clone() + }) + } +} + impl Expected { pub fn new(pos: Position, expect: &Expect) -> Expected { Expected { pos, expect: expect.clone(), an_or_a: true } @@ -32,6 +76,14 @@ impl Expected { pub fn and_or_a(&self, and_or_a: bool) -> Expected { Expected { an_or_a: and_or_a, ..self.clone() } } + + pub fn any(pos: Position) -> Expected { + Expected::new(pos, &Type { name: Name::any() }) + } + + pub fn none(pos: Position) -> Expected { + Expected::new(pos, &Type { name: Name::from(NONE) }) + } } impl AsRef for Expected { @@ -40,27 +92,15 @@ impl AsRef for Expected { } } -impl TryFrom<(&AST, &VarMapping)> for Expected { - type Error = Vec; - - /// Creates Expected from AST. - /// - /// If primitive or Constructor, constructs Type. - fn try_from((ast, mappings): (&AST, &VarMapping)) -> TypeResult { - let ast = match &ast.node { - Node::Block { statements } => statements.last().unwrap_or(ast), - _ => ast, - }; - - Ok(Expected::new(ast.pos, &Expect::try_from((ast, mappings))?)) +impl From<&Box> for Expected { + fn from(value: &Box) -> Self { + Self::from(value.deref()) } } -impl TryFrom<(&Box, &VarMapping)> for Expected { - type Error = Vec; - - fn try_from((ast, mappings): (&Box, &VarMapping)) -> TypeResult { - Expected::try_from((ast.deref(), mappings)) +impl From<&AST> for Expected { + fn from(ast: &AST) -> Expected { + Expected::new(ast.pos, &Expression { ast: ast.clone() }) } } @@ -75,34 +115,6 @@ pub enum Expect { Type { name: Name }, } -impl Any for Expect { - fn any() -> Self { Type { name: Name::any() } } -} - -impl TryFrom<(&AST, &VarMapping)> for Expect { - type Error = Vec; - - /// Also substitutes any identifiers with new ones from the environment if the environment - /// has a mapping. - /// This means that we forget about shadowed variables and continue with the new ones. - fn try_from((ast, mappings): (&AST, &VarMapping)) -> TypeResult { - let ast = ast.map(&|node: &Node| { - if let Node::Id { lit } = node { - if let Some(offset) = mappings.get(lit) { - // Always use name currently defined in environment - Node::Id { lit: format_var_map(lit, offset) } - } else { - node.clone() - } - } else { - node.clone() - } - }); - - Ok(Expression { ast }) - } -} - impl Display for Expected { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match &self.expect { @@ -115,7 +127,7 @@ impl Display for Expected { impl Display for Expect { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match &self { - Expression { ast } => write!(f, "`{}`", ast.node), + Expression { ast } => write!(f, "{}", ast.node), Collection { ty, .. } => write!(f, "{{{}}}", ty.and_or_a(false)), Tuple { elements } => { let elements: Vec = elements.iter().map(|a| a.and_or_a(false)).collect(); @@ -163,10 +175,6 @@ impl Expect { } } - pub fn none() -> Expect { - Type { name: Name::from(NONE) } - } - pub fn is_none(&self) -> bool { match &self { Type { name } => name.is_null(), @@ -177,10 +185,7 @@ impl Expect { #[cfg(test)] mod tests { - use std::collections::HashMap; - use std::convert::TryFrom; - - use crate::check::constrain::constraint::expected::Expect; + use crate::check::constrain::constraint::expected::{Expect, Expected}; use crate::common::position::{CaretPos, Position}; use crate::parse::ast::{AST, Node}; use crate::parse::parse_direct; @@ -188,10 +193,9 @@ mod tests { #[test] fn test_expected_from_int_constructor_call() { let ast = parse_direct("Int(10)").unwrap(); - let mappings = HashMap::new(); - let expect = Expect::try_from((&ast[0], &mappings)).unwrap(); + let expect = Expected::from(&ast[0]); - assert_eq!(expect, Expect::Expression { + assert_eq!(expect.expect, Expect::Expression { ast: AST::new( Position::new(CaretPos::new(1, 1), CaretPos::new(1, 8)), Node::FunctionCall { diff --git a/src/check/constrain/constraint/iterator.rs b/src/check/constrain/constraint/iterator.rs index beb6ba59..de1aa0bd 100644 --- a/src/check/constrain/constraint/iterator.rs +++ b/src/check/constrain/constraint/iterator.rs @@ -3,24 +3,23 @@ use std::collections::VecDeque; use crate::check::constrain::constraint::Constraint; use crate::check::constrain::constraint::expected::Expected; use crate::check::result::{TypeErr, TypeResult}; +use crate::common::position::Position; #[derive(Clone, Debug)] pub struct Constraints { - constraints: VecDeque, + pub pos: Position, + pub msg: String, + + pub(in super) constraints: VecDeque, } -impl From<&Vec> for Constraints { - fn from(constraints: &Vec) -> Self { - let constraints = VecDeque::from(constraints.clone()); - Constraints { constraints } +impl From<(Position, String, Vec)> for Constraints { + fn from((pos, msg, constraints): (Position, String, Vec)) -> Self { + Constraints { pos, msg, constraints: VecDeque::from(constraints) } } } impl Constraints { - pub fn new() -> Constraints { - Constraints { constraints: VecDeque::new() } - } - pub fn len(&self) -> usize { self.constraints.len() } pub fn pop_constr(&mut self) -> Option { self.constraints.pop_front() } @@ -52,7 +51,3 @@ impl Constraints { Ok(()) } } - -impl Default for Constraints { - fn default() -> Self { Constraints::new() } -} diff --git a/src/check/constrain/constraint/mod.rs b/src/check/constrain/constraint/mod.rs index dbf47be4..9c972149 100644 --- a/src/check/constrain/constraint/mod.rs +++ b/src/check/constrain/constraint/mod.rs @@ -1,5 +1,6 @@ use std::fmt::{Display, Error, Formatter}; +use crate::check::constrain::constraint::builder::VarMapping; use crate::check::constrain::constraint::expected::Expect::{Access, Function, Type}; use crate::check::constrain::constraint::expected::Expected; use crate::check::context::{clss, function}; @@ -32,6 +33,10 @@ impl Default for ConstrVariant { } } +pub(super) trait MapExp { + fn map_exp(&self, var_mapping: &VarMapping, global_var_mapping: &VarMapping) -> Self; +} + impl Display for Constraint { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { let superset = match &self.superset { @@ -43,6 +48,14 @@ impl Display for Constraint { } } +impl MapExp for Constraint { + fn map_exp(&self, var_mapping: &VarMapping, global_var_mapping: &VarMapping) -> Self { + let left = self.left.map_exp(var_mapping, global_var_mapping); + let right = self.right.map_exp(var_mapping, global_var_mapping); + Constraint { left, right, ..self.clone() } + } +} + impl Constraint { /// Create new constraint. /// @@ -53,14 +66,9 @@ impl Constraint { pub fn new_variant(msg: &str, parent: &Expected, child: &Expected, superset: ConstrVariant) -> Constraint { - Constraint { - left: parent.clone(), - right: child.clone(), - msg: String::from(msg), - is_flag: false, - is_sub: false, - superset: superset.clone(), - } + let msg = String::from(msg); + let (left, right) = (parent.clone(), child.clone()); + Constraint { left, right, msg, is_flag: false, is_sub: false, superset } } /// Flag constraint iff flagged is 0, else ignored. diff --git a/src/check/constrain/generate/call.rs b/src/check/constrain/generate/call.rs index db7339d1..23cf29a1 100644 --- a/src/check/constrain/generate/call.rs +++ b/src/check/constrain/generate/call.rs @@ -46,11 +46,7 @@ pub fn gen_call( }) .fold(env.clone(), |env, self_var| env.assigned_to(&self_var)); - constr.add( - "reassign", - &Expected::try_from((left, &constr.var_mapping))?, - &Expected::try_from((right, &constr.var_mapping))?, - ); + constr.add("reassign", &Expected::from(left), &Expected::from(right), env); generate(right, &env_assigned_to, ctx, constr)?; generate(left, &env_assigned_to, ctx, constr)?; Ok(env_assigned_to) @@ -63,17 +59,12 @@ pub fn gen_call( gen_vec(args, env, false, ctx, constr)?; Ok(if f_name == StringName::from(function::PRINT) { - args - .iter() - .map(|arg| Expected::try_from((arg, &constr.var_mapping))) - .collect::>>()? - .iter() - .map(|exp| Constraint::stringy("print", exp)) - .for_each(|cons| constr.add_constr(&cons)); + args.iter() + .map(|arg| Constraint::stringy("print", &Expected::from(arg))) + .for_each(|cons| constr.add_constr(&cons, env)); let name = Name::empty(); - let parent = Expected::new(ast.pos, &Type { name }); - constr.add("print", &parent, &Expected::try_from((ast, &constr.var_mapping))?); + constr.add("print", &Expected::new(ast.pos, &Type { name }), &Expected::from(ast), env); env.clone() } else if let Some(functions) = env.get_var(&f_name.name, &constr.var_mapping) { if !f_name.generics.is_empty() { @@ -83,27 +74,20 @@ pub fn gen_call( for (_, fun_exp) in functions { let last_pos = args.last().map_or_else(|| name.pos, |a| a.pos); - let args = args - .iter() - .map(|a| Expected::try_from((a, &constr.var_mapping))) - .collect::>()?; + let args = args.iter().map(Expected::from).collect(); let right = Expected::new(last_pos, &Function { name: f_name.clone(), args }); - constr.add("function call", &right, &fun_exp); + constr.add("function call", &right, &fun_exp, env); } env.clone() } else { // Resort to looking up in Context let fun = ctx.function(&f_name, ast.pos)?; - call_parameters(ast, &fun.arguments, &None, args, ctx, constr)?; + call_parameters(ast, &fun.arguments, &None, args, ctx, env, constr)?; let fun_ret_exp = Expected::new(ast.pos, &Type { name: fun.ret_ty }); // entire AST is either fun ret ty or statement - constr.add( - "function call", - &Expected::try_from((ast, &constr.var_mapping))?, - &fun_ret_exp, - ); + constr.add("function call", &Expected::from(ast), &fun_ret_exp, env); - check_raises_caught(constr, &fun.raises.names, env, ctx, ast.pos)?; + check_raises_caught(&fun.raises.names, env, ctx, ast.pos)?; env.clone() }) } @@ -114,28 +98,18 @@ pub fn gen_call( generate(range, env, ctx, constr)?; let name = Name::from(&HashSet::from([clss::INT, clss::SLICE])); - constr.add( - "index range", - &Expected::new(range.pos, &Type { name }), - &Expected::try_from((range, &constr.var_mapping))?, - ); + constr.add("index range", &Expected::new(range.pos, &Type { name }), &Expected::from(range), env); let (temp_type, env) = env.temp_var(); let temp_collection_type = Type { name: Name::from(temp_type.as_str()) }; let exp_col = Collection { ty: Box::from(Expected::new(ast.pos, &temp_collection_type)) }; let exp_col = Expected::new(ast.pos, &exp_col); - constr.add("type of indexed collection", - &exp_col, - &Expected::try_from((item, &constr.var_mapping))?, - ); + constr.add("type of indexed collection", &exp_col, &Expected::from(item), &env); // Must be after above constraint - constr.add( - "index of collection", - &Expected::new(ast.pos, &temp_collection_type), - &Expected::try_from((ast, &constr.var_mapping))?, - ); + let exp_col_ty = Expected::new(ast.pos, &temp_collection_type); + constr.add("index of collection", &exp_col_ty, &Expected::from(ast), &env); generate(item, &env, ctx, constr)?; Ok(env.clone()) @@ -174,6 +148,7 @@ fn call_parameters( self_arg: &Option, args: &[AST], ctx: &Context, + env: &Environment, constr: &mut ConstrBuilder, ) -> Constrained<()> { let args = if let Some(self_arg) = self_arg { @@ -195,7 +170,7 @@ fn call_parameters( let arg_exp = Expected::new(*pos, arg); let name = Name::from(&ctx.class(ty, *pos)?); - constr.add("call parameters", &Expected::new(*pos, &Type { name }), &arg_exp) + constr.add("call parameters", &Expected::new(*pos, &Type { name }), &arg_exp, env) } Left(fun_arg) if !fun_arg.has_default => { let pos = Position::new(self_ast.pos.end, self_ast.pos.end); @@ -239,12 +214,7 @@ fn property_call( } Node::FunctionCall { name, args } => { gen_vec(args, env, false, ctx, constr)?; - let args = vec![last_inst.clone()] - .iter() - .chain(args) - .map(|ast| Expected::try_from((ast, &constr.var_mapping))) - .collect::>()?; - + let args = vec![last_inst.clone()].iter().chain(args).map(Expected::from).collect(); let function = Function { name: StringName::try_from(name)?, args }; Expected::new(property.pos, &function) } @@ -256,7 +226,7 @@ fn property_call( let (instance, property) = (Box::from(ast.clone()), Box::from(acc)); AST::new(ast.pos, Node::PropertyCall { instance, property }) }); - let entire_call_as_ast = Expected::try_from((&entire_call_as_ast, &constr.var_mapping))?; + let entire_call_as_ast = Expected::from(&entire_call_as_ast); let ast_without_access = match instance.len().cmp(&1) { Ordering::Less => { @@ -272,16 +242,15 @@ fn property_call( } }; + let entity = Box::new(Expected::from(&ast_without_access)); + let msg = format!("access property of {entity}"); let access = Expected::new( ast_without_access.pos.union(access.pos), - &Access { - entity: Box::new(Expected::try_from((&ast_without_access, &constr.var_mapping))?), - name: Box::new(access), - }, + &Access { entity, name: Box::new(access) }, ); + constr.add(&msg, &access, &entire_call_as_ast, env); generate(&ast_without_access, env, ctx, constr)?; - constr.add("call property", &access, &entire_call_as_ast); Ok(env.clone()) } @@ -329,9 +298,7 @@ fn reassign_op( ctx: &Context, constr: &mut ConstrBuilder, ) -> Constrained { - let left = Box::from(left.clone()); - let right = Box::from(right.clone()); - + let (left, right) = (Box::from(left.clone()), Box::from(right.clone())); let right = Box::from(AST::new( ast.pos, match op { @@ -354,5 +321,6 @@ fn reassign_op( let node = Node::Reassign { left, right, op: NodeOp::Assign }; let simple_assign_ast = AST::new(ast.pos, node); generate(&simple_assign_ast, env, ctx, constr)?; + Ok(env.clone()) } diff --git a/src/check/constrain/generate/class.rs b/src/check/constrain/generate/class.rs index 49a11a8f..6fb2e8e5 100644 --- a/src/check/constrain/generate/class.rs +++ b/src/check/constrain/generate/class.rs @@ -2,7 +2,7 @@ use std::convert::TryFrom; use crate::check::constrain::constraint::builder::ConstrBuilder; use crate::check::constrain::generate::{Constrained, gen_vec, generate}; -use crate::check::constrain::generate::definition::identifier_from_var; +use crate::check::constrain::generate::definition::id_from_var; use crate::check::constrain::generate::env::Environment; use crate::check::context::arg::python::SELF; use crate::check::context::Context; @@ -30,7 +30,7 @@ pub fn gen_class( // Self is defined top level in type alias let var = AST::new(ty.pos, Id { lit: String::from(SELF) }); let name = Some(Name::try_from(isa)?); // For now assume super - let env = identifier_from_var(&var, &name, &None, false, ctx, constr, env)?; + let env = id_from_var(&var, &name, &None, false, ctx, constr, env)?; constrain_class_body(conditions, isa, &env, ctx, constr) } @@ -51,12 +51,14 @@ pub fn constrain_class_body( ctx: &Context, constr: &mut ConstrBuilder, ) -> Constrained { - let class_lvl = constr.new_set(); - let name = StringName::try_from(ty)?; let class_env = env.in_class(&name); gen_vec(statements, &class_env, true, ctx, constr)?; - constr.exit_set_to(class_lvl); - Ok(env.clone()) + // preserve mapping of self outside class to prevent contamination + if let Some(self_map) = constr.var_mapping.get(SELF) { + Ok(env.override_mapping(SELF, *self_map)) + } else { + Ok(env.clone()) + } } diff --git a/src/check/constrain/generate/collection.rs b/src/check/constrain/generate/collection.rs index 63417ec3..f1440d72 100644 --- a/src/check/constrain/generate/collection.rs +++ b/src/check/constrain/generate/collection.rs @@ -1,32 +1,28 @@ use std::convert::TryFrom; +use crate::check::constrain::constraint::{Constraint, MapExp}; use crate::check::constrain::constraint::builder::ConstrBuilder; -use crate::check::constrain::constraint::Constraint; -use crate::check::constrain::constraint::expected::{Expect, Expected}; use crate::check::constrain::constraint::expected::Expect::*; +use crate::check::constrain::constraint::expected::Expected; use crate::check::constrain::generate::{Constrained, gen_vec, generate}; use crate::check::constrain::generate::env::Environment; use crate::check::context::Context; use crate::check::ident::Identifier; -use crate::check::name::{Any, Name}; +use crate::check::name::Name; use crate::check::result::{TypeErr, TypeResult}; use crate::parse::ast::{AST, Node}; -pub fn gen_coll( - ast: &AST, - env: &Environment, - ctx: &Context, - constr: &mut ConstrBuilder, -) -> Constrained { +pub fn gen_coll(ast: &AST, env: &Environment, ctx: &Context, constr: &mut ConstrBuilder) + -> Constrained { match &ast.node { Node::Set { elements } | Node::List { elements } => { - let res = gen_vec(elements, env, false, ctx, constr)?; - constr_col(ast, constr, None)?; - Ok(res) + gen_vec(elements, env, false, ctx, constr)?; + constr_col(ast, env, constr, None)?; + Ok(env.clone()) } Node::Tuple { elements } => { let res = gen_vec(elements, env, env.is_def_mode, ctx, constr)?; - constr_col(ast, constr, None)?; + constr_col(ast, env, constr, None)?; Ok(res) } @@ -38,17 +34,16 @@ pub fn gen_coll( return Err(vec![TypeErr::new(cond.pos, &msg)]); }; - let item = Expected::try_from((left, &constr.var_mapping))?; + let item = Expected::from(left); let col_exp = Expected::new(right.pos, &Collection { ty: Box::new(item) }); - let cond_exp = Expected::try_from((right, &constr.var_mapping))?; - constr.add("comprehension collection type", &col_exp, &cond_exp); + constr.add("comprehension collection type", &col_exp, &Expected::from(right), env); generate(cond, &conds_env.is_def_mode(false), ctx, constr)?; if let Some(conditions) = conditions.strip_prefix(&[cond.clone()]) { for cond in conditions { generate(cond, &conds_env.is_def_mode(false), ctx, constr)?; - let cond = Expected::try_from((cond, &constr.var_mapping))?; - constr.add_constr(&Constraint::truthy("comprehension condition", &cond)); + let cond = Expected::from(cond); + constr.add_constr(&Constraint::truthy("comprehension condition", &cond), &conds_env); } } @@ -65,68 +60,59 @@ pub fn gen_coll( /// Generate constraint for collection by taking first element. /// /// The assumption here being that every element in the set has the same type. -pub fn constr_col( - collection: &AST, - constr: &mut ConstrBuilder, - temp_type: Option, -) -> TypeResult<()> { +pub fn constr_col(collection: &AST, env: &Environment, constr: &mut ConstrBuilder, temp_type: Option) + -> TypeResult<()> { let (msg, col) = match &collection.node { Node::Set { elements } | Node::List { elements } => { let ty = if let Some(first) = elements.first() { for element in elements { - let parent = Expected::try_from((first, &constr.var_mapping))?; - let child = Expected::try_from((element, &constr.var_mapping))?; - constr.add("collection item", &parent, &child) + constr.add("collection item", &Expected::from(first), &Expected::from(element), env) } - Box::from(Expected::new(first.pos, &Expect::try_from((first, &constr.var_mapping))?)) + Box::from(Expected::from(first)) } else { - Box::from(Expected::new(collection.pos, &Expect::any())) + Box::from(Expected::any(collection.pos)) }; ("collection", Collection { ty }) } Node::Tuple { elements } => { - let map = |ast: &AST| Expected::try_from((ast, &constr.var_mapping)); - let elements = elements.iter().map(map).collect::>()?; - ("tuple", Tuple { elements }) + ("tuple", Tuple { elements: elements.iter().map(Expected::from).collect() }) } - _ => { - let expect = if let Some(name) = temp_type { Type { name } } else { Expect::any() }; - let expected = Collection { ty: Box::from(Expected::new(collection.pos, &expect)) }; - ("collection", expected) + let ty = Box::from(if let Some(name) = temp_type { + Expected::new(collection.pos, &Type { name }) + } else { + Expected::any(collection.pos) + }); + ("collection", Collection { ty }) } }; let col_exp = Expected::new(collection.pos, &col); - constr.add(msg, &col_exp, &Expected::try_from((collection, &constr.var_mapping))?); + constr.add(msg, &col_exp, &Expected::from(collection), env); Ok(()) } /// Constrain lookup an collection. /// /// Adds constraint of collection of type lookup, and the given collection. -pub fn gen_collection_lookup( - lookup: &AST, - col: &AST, - env: &Environment, - constr: &mut ConstrBuilder, -) -> Constrained { +pub fn gen_collection_lookup(lookup: &AST, col: &AST, env: &Environment, constr: &mut ConstrBuilder) + -> Constrained { let mut env = env.clone(); // Make col constraint before inserting environment, in case shadowed here - let col_exp = Expected::try_from((col, &constr.var_mapping))?; + let col_exp = Expected::from(col).map_exp(&env.var_mapping, &constr.var_mapping); + for (mutable, var) in Identifier::try_from(lookup)?.fields(lookup.pos)? { constr.insert_var(&var); - env = env.insert_var(mutable, &var, &Expected::new(lookup.pos, &Expect::any()), &constr.var_mapping); + env = env.insert_var(mutable, &var, &Expected::any(lookup.pos), &constr.var_mapping); } - let col_ty_exp = Expected::new( - col.pos, - &Collection { ty: Box::from(Expected::try_from((lookup, &constr.var_mapping))?) }, - ); + let col_ty_exp = Expected::new(col.pos, &Collection { ty: Box::from(Expected::from(lookup)) }) + .map_exp(&env.var_mapping, &constr.var_mapping); + let constraint = Constraint::new("collection lookup", &col_ty_exp, &col_exp); + constr.add_constr_map(&constraint, &env.var_mapping, true); - constr.add("collection lookup", &col_ty_exp, &col_exp); Ok(env) } diff --git a/src/check/constrain/generate/control_flow.rs b/src/check/constrain/generate/control_flow.rs index c10161df..8f365c84 100644 --- a/src/check/constrain/generate/control_flow.rs +++ b/src/check/constrain/generate/control_flow.rs @@ -45,34 +45,29 @@ pub fn gen_flow( } Node::IfElse { cond, then, el: Some(el) } => { - let left = Expected::try_from((cond, &constr.var_mapping))?; - constr.add_constr(&Constraint::truthy("if else", &left)); + constr.add_constr(&Constraint::truthy("if condition", &Expected::from(cond)), env); generate(cond, env, ctx, constr)?; + let if_expr_exp = Expected::from(ast); - let if_expr_exp = Expected::try_from((ast, &constr.var_mapping))?; - - constr.new_set(); - let then_env = generate(then, env, ctx, constr)?; - let then_exp = Expected::try_from((then, &constr.var_mapping))?; + constr.branch_point(); + generate(then, env, ctx, constr)?; if env.is_expr { - constr.add("then branch equal to if", &then_exp, &if_expr_exp); + constr.add("then branch equal to if", &Expected::from(then), &if_expr_exp, env); } - constr.new_set(); - let else_env = generate(el, env, ctx, constr)?; + constr.branch("if else branch", el.pos); + generate(el, env, ctx, constr)?; if env.is_expr { - let el = Expected::try_from((el, &constr.var_mapping))?; - constr.add("else branch equal to if", &el, &then_exp); + constr.add("else branch equal to if", &Expected::from(el), &if_expr_exp, env); } - Ok(env.union(&then_env.intersect(&else_env))) + constr.reset_branches(); + Ok(env.clone()) } Node::IfElse { cond, then, .. } => { - let left = Expected::try_from((cond, &constr.var_mapping))?; - constr.add_constr(&Constraint::truthy("if else", &left)); + constr.add_constr(&Constraint::truthy("if condition", &Expected::from(cond)), env); generate(cond, env, ctx, constr)?; - constr.new_set(); generate(then, env, ctx, constr)?; Ok(env.clone()) } @@ -85,24 +80,17 @@ pub fn gen_flow( } Node::For { expr, col, body } => { - let loop_lvl = constr.new_set(); let col_env = generate(col, env, ctx, constr)?; - - let is_define_mode = col_env.is_def_mode; let lookup_env = gen_collection_lookup(expr, col, &col_env.is_def_mode(true), constr)?; - generate(body, &lookup_env.in_loop().is_def_mode(is_define_mode), ctx, constr)?; - constr.exit_set_to(loop_lvl); + generate(body, &lookup_env.in_loop().is_def_mode(false), ctx, constr)?; Ok(env.clone()) } Node::While { cond, body } => { - let while_lvl = constr.new_set(); - let cond_exp = Expected::try_from((cond, &constr.var_mapping))?; - constr.add_constr(&Constraint::truthy("while condition", &cond_exp)); + constr.add_constr(&Constraint::truthy("while condition", &Expected::from(cond)), env); generate(cond, env, ctx, constr)?; generate(body, &env.in_loop(), ctx, constr)?; - constr.exit_set_to(while_lvl); Ok(env.clone()) } @@ -115,30 +103,32 @@ pub fn gen_flow( fn constrain_cases(ast: &AST, expr: &Option, cases: &Vec, env: &Environment, ctx: &Context, constr: &mut ConstrBuilder) -> Constrained<()> { let is_define_mode = env.is_def_mode; - let exp_ast = Expected::try_from((ast, &constr.var_mapping))?; + constr.branch_point(); for case in cases { match &case.node { Node::Case { cond, body } => { - constr.new_set(); - + constr.branch("match arm", case.pos); let cond_env = generate(cond, &env.is_def_mode(true), ctx, constr)?; - generate(body, &cond_env.is_def_mode(is_define_mode), ctx, constr)?; if let Node::ExpressionType { expr: ref cond, .. } = cond.node { - if let Some(expr) = expr { - constr.add("match expression and arm condition", - &Expected::try_from((expr, &constr.var_mapping))?, - &Expected::try_from((cond, &constr.var_mapping))?, - ); + if let Some(expr) = &expr { + constr.add("arm body", &Expected::from(expr), &Expected::from(cond), env); } } - let exp_body = Expected::try_from((body, &constr.var_mapping))?; - constr.add("match arm body", &exp_body, &exp_ast); + generate(body, &cond_env.is_def_mode(is_define_mode), ctx, constr)?; + let exp_body = Expected::from(body); + constr.add("arm body", &exp_body, &Expected::from(ast), env); + + if env.is_expr { + constr.add("arm body and outer", &Expected::from(ast), &exp_body, env); + } } _ => return Err(vec![TypeErr::new(case.pos, "Expected case")]) } } + + constr.reset_branches(); Ok(()) } diff --git a/src/check/constrain/generate/definition.rs b/src/check/constrain/generate/definition.rs index 4238cb22..6342d4c2 100644 --- a/src/check/constrain/generate/definition.rs +++ b/src/check/constrain/generate/definition.rs @@ -5,8 +5,8 @@ use std::ops::Deref; use permutate::Permutator; use crate::check::constrain::constraint::builder::ConstrBuilder; -use crate::check::constrain::constraint::expected::{Expect, Expected}; use crate::check::constrain::constraint::expected::Expect::*; +use crate::check::constrain::constraint::expected::Expected; use crate::check::constrain::generate::{Constrained, generate}; use crate::check::constrain::generate::env::Environment; use crate::check::context::{clss, Context, function, LookupClass}; @@ -14,7 +14,7 @@ use crate::check::context::arg::SELF; use crate::check::context::clss::HasParent; use crate::check::context::field::Field; use crate::check::ident::Identifier; -use crate::check::name::{Any, match_name, Name, Nullable}; +use crate::check::name::{match_name, Name, Nullable}; use crate::check::name::true_name::TrueName; use crate::check::result::{TypeErr, TypeResult}; use crate::common::position::Position; @@ -28,8 +28,6 @@ pub fn gen_def( ) -> Constrained { match &ast.node { Node::FunDef { args: fun_args, ret: ret_ty, body, raises, id, .. } => { - let fun_lvl = constr.new_set(); - let non_nullable_class_vars: HashSet = match &id.node { Node::Id { lit } if *lit == function::INIT => { if let Some(class) = &env.class { @@ -77,7 +75,7 @@ pub fn gen_def( if let Some(ret_ty) = ret_ty { let name = Name::try_from(ret_ty)?; let ret_ty_raises_exp = Expected::new(body.pos, &Type { name: name.clone() }); - constr.add("fun body type", &ret_ty_raises_exp, &Expected::try_from((body, &constr.var_mapping))?); + constr.add("fun body type", &ret_ty_raises_exp, &Expected::from(body), env); let ret_ty_exp = Expected::new(ret_ty.pos, &Type { name }); let body_env = body_env.return_type(&ret_ty_exp).is_expr(true); @@ -98,7 +96,6 @@ pub fn gen_def( return Err(unassigned.iter().map(|msg| TypeErr::new(id.pos, msg)).collect()); } - constr.exit_set_to(fun_lvl); Ok(env.clone()) } Node::FunArg { .. } => { @@ -107,9 +104,9 @@ pub fn gen_def( Node::VariableDef { mutable, var, ty, expr: expression, .. } => if let Some(ty) = ty { let name = Name::try_from(ty)?; - identifier_from_var(var, &Some(name), expression, *mutable, ctx, constr, env) + id_from_var(var, &Some(name), expression, *mutable, ctx, constr, env) } else { - identifier_from_var(var, &None, expression, *mutable, ctx, constr, env) + id_from_var(var, &None, expression, *mutable, ctx, constr, env) } _ => Err(vec![TypeErr::new(ast.pos, "Expected definition")]), @@ -137,11 +134,15 @@ pub fn constrain_args( return Err(vec![TypeErr::new(arg.pos, &msg)]); } - let name = Some(Name::from(class_name)); // ignore type alias for now for self - env_with_args = identifier_from_var(var, &name, default, *mutable, ctx, constr, &env_with_args)? + let name = Some(if let Some(ty) = ty { + Name::try_from(ty)? + } else { + Name::from(class_name) + }); + env_with_args = id_from_var(var, &name, default, *mutable, ctx, constr, &env_with_args)? } else { let ty = if let Some(ty) = ty { Some(Name::try_from(ty)?) } else { None }; - env_with_args = identifier_from_var(var, &ty, default, *mutable, ctx, constr, &env_with_args)?; + env_with_args = id_from_var(var, &ty, default, *mutable, ctx, constr, &env_with_args)?; } } _ => return Err(vec![TypeErr::new(arg.pos, "Expected function argument")]), @@ -151,7 +152,7 @@ pub fn constrain_args( Ok(env_with_args.is_expr(exp_expression)) } -pub fn identifier_from_var( +pub fn id_from_var( var: &AST, ty: &Option, expr: &Option>, @@ -163,52 +164,48 @@ pub fn identifier_from_var( if let Some(expr) = expr { generate(expr, &env.is_expr(true), ctx, constr)?; } - let mut env_with_var = env.clone(); + let mut env = env.clone(); let identifier = Identifier::try_from(var.deref())?.as_mutable(mutable); if let Some(ty) = ty { for (f_name, (f_mut, name)) in match_name(&identifier, ty, var.pos)? { let ty = Expected::new(var.pos, &Type { name: name.clone() }); constr.insert_var(&f_name); - env_with_var = env_with_var.insert_var(mutable && f_mut, &f_name, &ty, &constr.var_mapping); + env = env.insert_var(mutable && f_mut, &f_name, &ty, &constr.var_mapping); } } else { - let any = Expected::new(var.pos, &Expect::any()); + let any = Expected::any(var.pos); for (f_mut, f_name) in identifier.fields(var.pos)? { constr.insert_var(&f_name); - env_with_var = env_with_var.insert_var(mutable && f_mut, &f_name, &any, &constr.var_mapping); + env = env.insert_var(mutable && f_mut, &f_name, &any, &constr.var_mapping); } }; if identifier.is_tuple() { - let tup_exps = identifier_to_tuple(var.pos, &identifier, &env_with_var, constr)?; + let tup_exps = identifier_to_tuple(var.pos, &identifier, &env, constr)?; if let Some(ty) = ty { let ty_exp = Expected::new(var.pos, &Type { name: ty.clone() }); for tup_exp in &tup_exps { - constr.add("type and tuple", &ty_exp, tup_exp); + constr.add("type and tuple", &ty_exp, tup_exp, &env); } } if let Some(expr) = expr { - let expr_expt = Expected::try_from((expr, &constr.var_mapping))?; for tup_exp in &tup_exps { - constr.add("tuple and expression", &expr_expt, tup_exp); + constr.add("tuple and expression", &Expected::from(expr), tup_exp, &env); } } } - let var_expect = Expected::try_from((var, &constr.var_mapping))?; if let Some(ty) = ty { let ty_exp = Expected::new(var.pos, &Type { name: ty.clone() }); - constr.add("variable and type", &ty_exp, &var_expect); + constr.add("variable and type", &ty_exp, &Expected::from(var), &env); } if let Some(expr) = expr { - let expr_expect = Expected::try_from((expr, &constr.var_mapping))?; let msg = format!("variable and expression: `{}`", expr.node); - constr.add(&msg, &var_expect, &expr_expect); + constr.add(&msg, &Expected::from(var), &Expected::from(expr), &env); } - - Ok(env_with_var) + Ok(env) } // Returns every possible tuple. Elements of a tuple are not to be confused with diff --git a/src/check/constrain/generate/env.rs b/src/check/constrain/generate/env.rs index 2b6431f3..547b21c2 100644 --- a/src/check/constrain/generate/env.rs +++ b/src/check/constrain/generate/env.rs @@ -11,6 +11,7 @@ pub struct Environment { pub in_fun: bool, pub is_expr: bool, pub is_def_mode: bool, + pub is_destruct_mode: bool, pub return_type: Option, pub raises_caught: HashSet, @@ -41,30 +42,42 @@ impl Environment { Environment { is_def_mode, ..self.clone() } } + pub fn is_destruct_mode(&self, is_destruct_mode: bool) -> Self { + Environment { is_destruct_mode, ..self.clone() } + } + pub fn is_expr(&self, is_expr: bool) -> Environment { Environment { is_expr, ..self.clone() } } + pub fn override_mapping(&self, var: &str, mapping: usize) -> Self { + let mut var_mapping = self.var_mapping.clone(); + var_mapping.insert(String::from(var), mapping); + Environment { var_mapping, ..self.clone() } + } + /// Insert a variable. /// /// If the var was previously defined, it is renamed, and the rename mapping is stored. /// In future, if we get a variable, if it was renamed, the mapping is returned instead. - pub fn insert_var(&self, mutable: bool, var: &str, expect: &Expected, var_mappings: &VarMapping) -> Environment { + pub fn insert_var(&self, mutable: bool, var: &str, expect: &Expected, var_mapping: &VarMapping) -> Environment { let expected_set = vec![(mutable, expect.clone())].into_iter().collect::>(); let mut vars = self.vars.clone(); let offset = if let Some(offset) = self.var_mapping.get(var) { - *offset - } else if let Some(offset) = var_mappings.get(var) { + *offset + 1 + } else if let Some(offset) = var_mapping.get(var) { *offset } else { - 0usize + 0_usize }; let mut var_mappings = self.var_mapping.clone(); var_mappings.insert(String::from(var), offset); - vars.insert(format_var_map(var, &offset), expected_set); + let mapped_var = format_var_map(var, &offset); + trace!("Inserted {var} in environment: {var} => {mapped_var}"); + vars.insert(mapped_var, expected_set); Environment { vars, var_mapping: var_mappings, ..self.clone() } } @@ -97,10 +110,10 @@ impl Environment { /// If not found, use variable directly in lookup. /// /// Return true variable [TrueName], whether it's mutable and it's expected value. - pub fn get_var(&self, var: &str, var_mappings: &VarMapping) -> Option> { + pub fn get_var(&self, var: &str, var_mapping: &VarMapping) -> Option> { let var_name = if let Some(offset) = self.var_mapping.get(var) { format_var_map(var, offset) - } else if let Some(offset) = var_mappings.get(var) { + } else if let Some(offset) = var_mapping.get(var) { format_var_map(var, offset) } else { String::from(var) @@ -109,51 +122,10 @@ impl Environment { self.vars.get(&var_name).cloned() } - /// Union between two environments - /// - /// Combines all variables. - /// Variable mappings are discarded. - pub fn union(&self, other: &Environment) -> Environment { + pub fn remove_var(&self, var: &str) -> Self { let mut vars = self.vars.clone(); - for (key, other_set) in &other.vars { - if let Some(this_set) = vars.get(key) { - let new_set = this_set.union(other_set).cloned().collect(); - vars.insert(key.clone(), new_set); - } else { - vars.insert(key.clone(), other_set.clone()); - } - } - - Environment { vars, var_mapping: VarMapping::new(), ..self.clone() } - } - - /// Intersection between two environments. - /// - /// If both environments contain the same variable, variable gets assigned - /// both the expected. Variables that are only present in one of the - /// environments are discarded. - /// - /// Only intersect vars, all other fields of other environment are - /// discarded. - /// - /// Variable mappings are discarded. - pub fn intersect(&self, other: &Environment) -> Environment { - let keys = self.vars.keys().filter(|key| other.vars.contains_key(*key)); - let mut vars = HashMap::new(); - for key in keys { - match (self.vars.get(key), other.vars.get(key)) { - (Some(l_exp), Some(r_exp)) => { - let union = l_exp.union(r_exp); - vars.insert(String::from(key), union.cloned().collect::>()); - } - (Some(exp), None) | (None, Some(exp)) => { - vars.insert(String::from(key), exp.clone()); - } - _ => {} - } - } - - Environment { vars, var_mapping: VarMapping::new(), ..self.clone() } + vars.remove(var); + Environment { vars, ..self.clone() } } /// Get a name for a temporary type. diff --git a/src/check/constrain/generate/expression.rs b/src/check/constrain/generate/expression.rs index b732bff5..7157b6bc 100644 --- a/src/check/constrain/generate/expression.rs +++ b/src/check/constrain/generate/expression.rs @@ -1,9 +1,9 @@ use std::convert::TryFrom; use crate::check::constrain::constraint::builder::ConstrBuilder; -use crate::check::constrain::constraint::expected::{Expect, Expected}; +use crate::check::constrain::constraint::expected::Expected; use crate::check::constrain::generate::{Constrained, generate}; -use crate::check::constrain::generate::definition::{constrain_args, identifier_from_var}; +use crate::check::constrain::generate::definition::{constrain_args, id_from_var}; use crate::check::constrain::generate::env::Environment; use crate::check::context::Context; use crate::check::name::Name; @@ -26,18 +26,14 @@ pub fn gen_expr( match_id(expr, ty, *mutable, env, ctx, constr), Node::Id { .. } => match_id(ast, &None, false, env, ctx, constr), Node::Question { left, right } => { - constr.add( - "question", - &Expected::try_from((left, &constr.var_mapping))?, - &Expected::new(left.pos, &Expect::none()), - ); + constr.add("question", &Expected::from(left), &Expected::none(left.pos), env); generate(left, env, ctx, constr)?; generate(right, env, ctx, constr)?; Ok(env.clone()) } Node::Pass => if let Some(expected_ret_ty) = &env.return_type { - constr.add("pass", &Expected::new(ast.pos, &Expect::none()), expected_ret_ty); + constr.add("pass", &Expected::none(ast.pos), expected_ret_ty, env); Ok(env.clone()) } else { Ok(env.clone()) @@ -51,7 +47,9 @@ fn match_id(ast: &AST, ty: &OptAST, mutable: bool, env: &Environment, ctx: &Cont match &ast.node { Node::Id { lit } => if env.is_def_mode { let ty = if let Some(ty) = ty { Some(Name::try_from(ty)?) } else { None }; - identifier_from_var(ast, &ty, &None, mutable, ctx, constr, env) + id_from_var(ast, &ty, &None, mutable, ctx, constr, env) + } else if env.is_destruct_mode { + Ok(env.remove_var(lit)) } else if env.get_var(lit, &constr.var_mapping).is_some() { Ok(env.clone()) } else { diff --git a/src/check/constrain/generate/mod.rs b/src/check/constrain/generate/mod.rs index 84e0c078..574851d6 100644 --- a/src/check/constrain/generate/mod.rs +++ b/src/check/constrain/generate/mod.rs @@ -28,7 +28,7 @@ mod resources; mod statement; mod ty; -mod env; +pub(super) mod env; pub type Constrained = Result>; diff --git a/src/check/constrain/generate/operation.rs b/src/check/constrain/generate/operation.rs index 6fdb12e0..7f08a822 100644 --- a/src/check/constrain/generate/operation.rs +++ b/src/check/constrain/generate/operation.rs @@ -2,15 +2,15 @@ use std::convert::TryFrom; use crate::check::constrain::constraint::builder::ConstrBuilder; use crate::check::constrain::constraint::Constraint; -use crate::check::constrain::constraint::expected::{Expect, Expected}; use crate::check::constrain::constraint::expected::Expect::*; +use crate::check::constrain::constraint::expected::Expected; use crate::check::constrain::generate::{Constrained, gen_vec, generate}; use crate::check::constrain::generate::collection::{constr_col, gen_collection_lookup}; use crate::check::constrain::generate::env::Environment; use crate::check::context::{Context, LookupClass}; use crate::check::context::clss::{BOOL, FLOAT, INT, RANGE, SLICE, STRING}; use crate::check::context::function::{ADD, DIV, EQ, FDIV, GE, GEQ, LE, LEQ, MOD, MUL, NEQ, POW, SQRT, SUB}; -use crate::check::name::{Any, Name}; +use crate::check::name::Name; use crate::check::name::string_name::StringName; use crate::check::name::true_name::TrueName; use crate::check::result::TypeErr; @@ -24,7 +24,7 @@ pub fn gen_op( ) -> Constrained { match &ast.node { Node::In { left, right } => { - constr_col(right, constr, None)?; + constr_col(right, env, constr, None)?; gen_collection_lookup(left, right, env, constr)?; generate(right, env, ctx, constr)?; @@ -46,19 +46,16 @@ pub fn gen_op( Node::Str { expressions, .. } => { gen_vec(expressions, env, false, ctx, constr)?; for expr in expressions { - let c = Constraint::stringy("string", &Expected::try_from((expr, &constr.var_mapping))?); - constr.add_constr(&c); + constr.add_constr(&Constraint::stringy("string", &Expected::from(expr)), env); } primitive(ast, STRING, env, constr) } Node::Bool { .. } => { - let truthy = Constraint::truthy("bool", &Expected::try_from((ast, &constr.var_mapping))?); - constr.add_constr(&truthy); + constr.add_constr(&Constraint::truthy("bool", &Expected::from(ast)), env); primitive(ast, BOOL, env, constr) } Node::Undefined => { - let undef = Constraint::undefined("undefined", &Expected::try_from((ast, &constr.var_mapping))?); - constr.add_constr(&undef); + constr.add_constr(&Constraint::undefined("undefined", &Expected::from(ast)), env); Ok(env.clone()) } @@ -80,59 +77,44 @@ pub fn gen_op( Node::AddU { expr } | Node::SubU { expr } => generate(expr, env, ctx, constr), Node::Sqrt { expr } => { let ty = Type { name: Name::from(FLOAT) }; - constr.add( - "square root", - &Expected::try_from((ast, &constr.var_mapping))?, - &Expected::new(ast.pos, &ty), - ); + constr.add("square root", &Expected::from(ast), &Expected::new(ast.pos, &ty), env); - let access = Expected::new( - expr.pos, - &Access { - entity: Box::new(Expected::try_from((expr, &constr.var_mapping))?), - name: Box::from(Expected::new( - expr.pos, - &Function { - name: StringName::from(SQRT), - args: vec![Expected::try_from((expr, &constr.var_mapping))?], - }, - )), - }, - ); - constr.add("square root", &Expected::try_from((ast, &constr.var_mapping))?, &access); + let access = Expected::new(expr.pos, &Access { + entity: Box::new(Expected::from(expr)), + name: Box::from(Expected::new( + expr.pos, + &Function { name: StringName::from(SQRT), args: vec![Expected::from(expr)] }, + )), + }); + constr.add("square root", &Expected::from(ast), &access, env); generate(expr, env, ctx, constr) } Node::BOneCmpl { expr } => { - let left = Expected::try_from((expr, &constr.var_mapping))?; - constr.add("binary compliment", &left, &Expected::new(expr.pos, &Expect::any())); + constr.add("binary compliment", &Expected::from(expr), &Expected::any(expr.pos), env); generate(expr, env, ctx, constr)?; Ok(env.clone()) } Node::BAnd { left, right } | Node::BOr { left, right } | Node::BXOr { left, right } => { - let l_exp = Expected::try_from((left, &constr.var_mapping))?; - constr.add("binary logical op", &l_exp, &Expected::new(left.pos, &Expect::any())); - - let l_exp = Expected::try_from((right, &constr.var_mapping))?; - constr.add("binary logical op", &l_exp, &Expected::new(right.pos, &Expect::any())); + constr.add("binary logical op", &Expected::from(left), &Expected::any(left.pos), env); + constr.add("binary logical op", &Expected::from(right), &Expected::any(right.pos), env); bin_op(left, right, env, ctx, constr) } Node::BLShift { left, right } | Node::BRShift { left, right } => { - let l_exp = Expected::try_from((left, &constr.var_mapping))?; - constr.add("binary shift", &l_exp, &Expected::new(right.pos, &Expect::any())); + constr.add("binary shift", &Expected::from(left), &Expected::any(right.pos), env); let name = Name::from(INT); - let l_exp = Expected::try_from((right, &constr.var_mapping))?; - constr.add("binary shift", &l_exp, &Expected::new(right.pos, &Type { name })); + let l_exp = Expected::from(right); + constr.add("binary shift", &l_exp, &Expected::new(right.pos, &Type { name }), env); bin_op(left, right, env, ctx, constr) } Node::Is { left, right } | Node::IsN { left, right } => { let bool = Expected::new(ast.pos, &Type { name: Name::from(BOOL) }); - constr.add("and", &Expected::try_from((ast, &constr.var_mapping))?, &bool); + constr.add("and", &Expected::from(ast), &bool, env); bin_op(left, right, env, ctx, constr) } Node::IsA { left, right } | Node::IsNA { left, right } => if let Node::Id { .. } = right.node { @@ -149,27 +131,18 @@ pub fn gen_op( Node::Not { expr } => { let bool = Expected::new(ast.pos, &Type { name: Name::from(BOOL) }); - constr.add("and", &Expected::try_from((ast, &constr.var_mapping))?, &bool); - constr.add_constr(&Constraint::truthy( - "not", - &Expected::try_from((expr, &constr.var_mapping))?, - )); + constr.add("and", &Expected::from(ast), &bool, env); + constr.add_constr(&Constraint::truthy("not", &Expected::from(expr)), env); + generate(expr, env, ctx, constr)?; Ok(env.clone()) } Node::And { left, right } | Node::Or { left, right } => { let bool = Expected::new(ast.pos, &Type { name: Name::from(BOOL) }); - constr.add("and", &Expected::try_from((ast, &constr.var_mapping))?, &bool); - - constr.add_constr(&Constraint::truthy( - "and", - &Expected::try_from((left, &constr.var_mapping))?, - )); - constr.add_constr(&Constraint::truthy( - "and", - &Expected::try_from((right, &constr.var_mapping))?, - )); + constr.add("and", &Expected::from(ast), &bool, env); + constr.add_constr(&Constraint::truthy("and", &Expected::from(left)), env); + constr.add_constr(&Constraint::truthy("and", &Expected::from(right)), env); bin_op(left, right, env, ctx, constr) } @@ -197,27 +170,15 @@ pub fn constr_range( let name = Name::from(INT); let int_exp = &Expected::new(from.pos, &Type { name }); - constr.add( - &format!("{range_slice} from"), - &Expected::try_from((from, &constr.var_mapping))?, - int_exp, - ); - constr.add( - &format!("{range_slice} to"), - &Expected::try_from((to, &constr.var_mapping))?, - int_exp, - ); + constr.add(&format!("{range_slice} from"), &Expected::from(from), int_exp, env); + constr.add(&format!("{range_slice} to"), &Expected::from(to), int_exp, env); if let Some(step) = step { - constr.add( - &format!("{range_slice} step"), - &Expected::try_from((step, &constr.var_mapping))?, - int_exp, - ); + constr.add(&format!("{range_slice} step"), &Expected::from(step), int_exp, env); } if contr_coll { let col = Expected::new(ast.pos, &Collection { ty: Box::from(int_exp.clone()) }); - constr.add("range collection", &col, &Expected::try_from((ast, &constr.var_mapping))?); + constr.add("range collection", &col, &Expected::from(ast), env); } generate(from, env, ctx, constr)?; @@ -227,12 +188,8 @@ pub fn constr_range( } fn primitive(ast: &AST, ty: &str, env: &Environment, constr: &mut ConstrBuilder) -> Constrained { - let name = Name::from(ty); - constr.add( - format!("{ty} primitive").as_str(), - &Expected::try_from((ast, &constr.var_mapping))?, - &Expected::new(ast.pos, &Type { name }), - ); + let msg = format!("{ty} primitive"); + constr.add(&msg, &Expected::from(ast), &Expected::new(ast.pos, &Type { name: Name::from(ty) }), env); Ok(env.clone()) } @@ -245,27 +202,7 @@ fn impl_magic( ctx: &Context, constr: &mut ConstrBuilder, ) -> Constrained { - constr.add( - format!("{fun} operation").as_str(), - &Expected::try_from((ast, &constr.var_mapping))?, - &Expected::new( - left.pos, - &Access { - entity: Box::new(Expected::try_from((left, &constr.var_mapping))?), - name: Box::new(Expected::new( - left.pos, - &Function { - name: StringName::from(fun), - args: vec![ - Expected::try_from((left, &constr.var_mapping))?, - Expected::try_from((right, &constr.var_mapping))?, - ], - }, - )), - }, - ), - ); - + constr.add(format!("{fun} operation").as_str(), &Expected::from(ast), &access(fun, left, right), env); gen_vec(&[right.clone(), left.clone()], env, env.is_def_mode, ctx, constr) } @@ -279,38 +216,25 @@ fn impl_bool_op( constr: &mut ConstrBuilder, ) -> Constrained { if fun != EQ && fun != NEQ { - constr.add( - "bool operation", - &Expected::try_from((ast, &constr.var_mapping))?, - &Expected::new( - left.pos, - &Access { - entity: Box::new(Expected::try_from((left, &constr.var_mapping))?), - name: Box::new(Expected::new( - left.pos, - &Function { - name: StringName::from(fun), - args: vec![ - Expected::try_from((left, &constr.var_mapping))?, - Expected::try_from((right, &constr.var_mapping))?, - ], - }, - )), - }, - ), - ); + constr.add("bool operation", &Expected::from(ast), &access(fun, left, right), env); } let ty = Type { name: Name::from(BOOL) }; - constr.add( - "bool operation", - &Expected::try_from((ast, &constr.var_mapping))?, - &Expected::new(ast.pos, &ty), - ); - + constr.add("bool operation", &Expected::from(ast), &Expected::new(ast.pos, &ty), env); bin_op(left, right, env, ctx, constr) } +fn access(fun: &str, left: &AST, right: &AST) -> Expected { + let name = StringName::from(fun); + Expected::new(left.pos, &Access { + entity: Box::new(Expected::from(left)), + name: Box::new(Expected::new( + left.pos, + &Function { name, args: vec![Expected::from(left), Expected::from(right)] }, + )), + }) +} + fn bin_op(left: &AST, right: &AST, env: &Environment, ctx: &Context, constr: &mut ConstrBuilder) -> Constrained { gen_vec(&[right.clone(), left.clone()], env, false, ctx, constr) } diff --git a/src/check/constrain/generate/resources.rs b/src/check/constrain/generate/resources.rs index dba4a196..26c9f940 100644 --- a/src/check/constrain/generate/resources.rs +++ b/src/check/constrain/generate/resources.rs @@ -1,13 +1,13 @@ use std::convert::TryFrom; use crate::check::constrain::constraint::builder::ConstrBuilder; -use crate::check::constrain::constraint::expected::{Expect, Expected}; use crate::check::constrain::constraint::expected::Expect::Type; +use crate::check::constrain::constraint::expected::Expected; use crate::check::constrain::generate::{Constrained, generate}; -use crate::check::constrain::generate::definition::identifier_from_var; +use crate::check::constrain::generate::definition::id_from_var; use crate::check::constrain::generate::env::Environment; use crate::check::context::Context; -use crate::check::name::{Any, Name}; +use crate::check::name::Name; use crate::check::result::TypeErr; use crate::parse::ast::{AST, Node}; @@ -19,45 +19,29 @@ pub fn gen_resources( ) -> Constrained { match &ast.node { Node::With { resource, alias: Some((alias, mutable, ty)), expr } => { - let with_lvl = constr.new_set(); - let resource_exp = Expected::try_from((resource, &constr.var_mapping))?; - constr.add("with as", &resource_exp, &Expected::try_from((alias, &constr.var_mapping))?); - constr.add("with as", &resource_exp, &Expected::new(resource.pos, &Expect::any())); + constr.add("with alias", &Expected::from(resource), &Expected::from(alias), env); + constr.add("with resource", &Expected::from(resource), &Expected::any(resource.pos), env); if let Some(ty) = ty { let ty_exp = Type { name: Name::try_from(ty)? }; - constr.add("with as", &resource_exp, &Expected::new(ty.pos, &ty_exp)); + constr.add("with alias type", &Expected::from(resource), &Expected::new(ty.pos, &ty_exp), env); } - let resource_env = generate(resource, env, ctx, constr)?; + let resource_env = generate(resource, &env.is_destruct_mode(true), ctx, constr)? + .is_destruct_mode(false) + .is_def_mode(true); - constr.new_set(); + constr.branch_point(); let ty = if let Some(ty) = ty { Some(Name::try_from(ty)?) } else { None }; - let resource_env = identifier_from_var( - alias, - &ty, - &Some(alias.clone()), - *mutable, - ctx, - constr, - &resource_env.is_def_mode(true), - )?; + let resource_env = id_from_var(alias, &ty, &Some(alias.clone()), *mutable, ctx, constr, &resource_env)?; + generate(expr, &resource_env.is_def_mode(false), ctx, constr)?; - generate(expr, &resource_env, ctx, constr)?; - constr.exit_set_to(with_lvl); Ok(env.clone()) } Node::With { resource, expr, .. } => { - let with_lvl = constr.new_set(); - constr.add( - "with", - &Expected::try_from((resource, &constr.var_mapping))?, - &Expected::new(resource.pos, &Expect::any()), - ); + constr.add("with", &Expected::from(resource), &Expected::any(resource.pos), env); let resource_env = generate(resource, env, ctx, constr)?; - - constr.exit_set_to(with_lvl); generate(expr, &resource_env, ctx, constr)?; Ok(env.clone()) } diff --git a/src/check/constrain/generate/statement.rs b/src/check/constrain/generate/statement.rs index f5f2fb91..06eea06e 100644 --- a/src/check/constrain/generate/statement.rs +++ b/src/check/constrain/generate/statement.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use std::convert::TryFrom; use std::iter::FromIterator; use crate::check::constrain::constraint::builder::ConstrBuilder; @@ -23,7 +22,7 @@ pub fn gen_stmt( Node::Raise { error } => match &error.node { Node::FunctionCall { name, .. } => if let Node::Id { lit } = &name.node { let raises = HashSet::from_iter([TrueName::from(lit.as_str())]); - check_raises_caught(constr, &raises, env, ctx, ast.pos)?; + check_raises_caught(&raises, env, ctx, ast.pos)?; Ok(env.clone()) } else { Err(vec![TypeErr::new(name.pos, &format!("Malformed raise: {}", name.node))]) @@ -41,11 +40,7 @@ pub fn gen_stmt( Node::Return { expr } => { if let Some(expected_ret_ty) = &env.return_type { generate(expr, env, ctx, constr)?; - constr.add( - "return", - expected_ret_ty, - &Expected::try_from((expr, &constr.var_mapping))?, - ); + constr.add("return", expected_ret_ty, &Expected::from(expr), env); Ok(env.clone()) } else if !env.in_fun { Err(vec![TypeErr::new(ast.pos, "Return outside function")]) @@ -57,13 +52,13 @@ pub fn gen_stmt( } } -/// Check whether a set of raises is properly dealt with. +/// Check whether a set of raises is properly dealt with if in function body. /// /// Makes use of the [Environment::raises_caught] field. /// For each raises, checks whether it or a parent of it is caught. /// If we are a top-level script, we perform no check as raises do not need to be caught here. -pub fn check_raises_caught(constr: &ConstrBuilder, raises: &HashSet, env: &Environment, ctx: &Context, pos: Position) -> Constrained<()> { - if !constr.is_top_level() { +pub fn check_raises_caught(raises: &HashSet, env: &Environment, ctx: &Context, pos: Position) -> Constrained<()> { + if env.in_fun { let errs: Vec = raises.iter() .filter(|raise_name| { !if let Ok(raise_class) = ctx.class(*raise_name, pos) { @@ -79,5 +74,6 @@ pub fn check_raises_caught(constr: &ConstrBuilder, raises: &HashSet, e if !errs.is_empty() { return Err(errs); } } + Ok(()) } diff --git a/src/check/constrain/mod.rs b/src/check/constrain/mod.rs index 2e89b255..289ab094 100644 --- a/src/check/constrain/mod.rs +++ b/src/check/constrain/mod.rs @@ -64,7 +64,6 @@ mod tests { } #[test] - #[ignore] // not sure if the check stage should pass as of yet fn it_stmt_as_expression_none() { let src = "def a := if True then 10 else None"; let ast = parse(src).unwrap(); diff --git a/src/check/constrain/unify/function.rs b/src/check/constrain/unify/function.rs index 4a682ff3..2d614045 100644 --- a/src/check/constrain/unify/function.rs +++ b/src/check/constrain/unify/function.rs @@ -172,11 +172,13 @@ fn function_access( } } - let added = unify_fun_arg(&possible_args, args, constraints, accessed.pos)?; + let added = unify_fun_arg(entity_name, name, &possible_args, args, constraints, accessed.pos)?; unify_link(constraints, finished, ctx, total + added + pushed) } fn unify_fun_arg( + entity_name: &Name, + name: &StringName, ctx_f_args: &[HashSet], args: &[Expected], constr: &mut Constraints, @@ -206,11 +208,16 @@ fn unify_fun_arg( added += 1; } EitherOrBoth::Left(fun_arg) if !fun_arg.iter().any(|a| !a.has_default) => { - let msg = format!("Expected argument for '{}'", comma_delm(fun_arg)); + let msg = format!("Expected argument for '{}' in Method {name} of {entity_name}", + comma_delm(fun_arg)); return Err(vec![TypeErr::new(pos, &msg)]); } EitherOrBoth::Right(_) => { - let msg = format!("Function takes only {} {}", ctx_f_args.len(), if ctx_f_args.len() == 1 { "argument" } else { "arguments" }); + let msg = format!("Method {name} of {entity_name} takes only {} {}, received {}: {}", + ctx_f_args.len(), + if ctx_f_args.len() == 1 { "argument" } else { "arguments" }, + args.len(), + comma_delm(args)); return Err(vec![TypeErr::new(pos, &msg)]); } _ => {} diff --git a/src/check/constrain/unify/mod.rs b/src/check/constrain/unify/mod.rs index 9fd37051..15b49de3 100644 --- a/src/check/constrain/unify/mod.rs +++ b/src/check/constrain/unify/mod.rs @@ -21,7 +21,7 @@ pub fn unify(all_constraints: &[Constraints], ctx: &Context) -> Unified { let (_, errs): (Vec<_>, Vec<_>) = all_constraints .iter() .map(|constraints| { - trace!("[unifying set {}\\{}]", count, all_constraints.len()); + trace!("[unifying set {}\\{}: {} (branched at {})]", count, all_constraints.len(), constraints.msg, constraints.pos); count += 1; unify_link(&mut constraints.clone(), &mut finished, ctx, constraints.len()).map_err(|e| { trace!( diff --git a/src/generate/convert/control_flow.rs b/src/generate/convert/control_flow.rs index 93bf4d61..c0619abb 100644 --- a/src/generate/convert/control_flow.rs +++ b/src/generate/convert/control_flow.rs @@ -8,14 +8,14 @@ use crate::generate::result::{GenResult, UnimplementedErr}; pub fn convert_cntrl_flow(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context) -> GenResult { Ok(match &ast.node { NodeTy::IfElse { cond, then, el } => { - let cond = Box::from(convert_node(cond, imp, &state.is_last_must_be_ret(false).must_assign_to(None), ctx)?); + let cond = Box::from(convert_node(cond, imp, &state.is_last_must_be_ret(false).must_assign_to(None, None), ctx)?); match el { Some(el) => if ast.ty.is_some() && is_valid_in_ternary(then, el) { let state = state .is_last_must_be_ret(false) .remove_ret(true) - .must_assign_to(None); + .must_assign_to(None, None); Core::Ternary { cond, @@ -36,14 +36,14 @@ pub fn convert_cntrl_flow(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &C } } NodeTy::Match { cond, cases: match_cases } => { - let expr = Box::from(convert_node(cond, imp, &state.is_last_must_be_ret(false).must_assign_to(None), ctx)?); + let expr = Box::from(convert_node(cond, imp, &state.is_last_must_be_ret(false).must_assign_to(None, None), ctx)?); let mut cases = vec![]; for case in match_cases { if let NodeTy::Case { cond, body } = &case.node { if let NodeTy::ExpressionType { expr, .. } = &cond.node { cases.push(Core::Case { - expr: Box::from(convert_node(expr.as_ref(), imp, &state.is_last_must_be_ret(false).must_assign_to(None), ctx)?), + expr: Box::from(convert_node(expr.as_ref(), imp, &state.is_last_must_be_ret(false).must_assign_to(None, None), ctx)?), body: Box::from(convert_node(body.as_ref(), imp, state, ctx)?), }) } diff --git a/src/generate/convert/definition.rs b/src/generate/convert/definition.rs index d8649f77..7ec618a5 100644 --- a/src/generate/convert/definition.rs +++ b/src/generate/convert/definition.rs @@ -53,7 +53,7 @@ pub fn convert_def(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context) match expr_core { Core::IfElse { .. } | Core::Match { .. } => { // redo convert but with assign to state - let state = state.must_assign_to(Option::from(&var.clone())); + let state = state.must_assign_to(Some(&var.clone()), expr.ty.clone()); return convert_node(expr, imp, &state, ctx); } _ => Some(Box::from(expr_core)) diff --git a/src/generate/convert/handle.rs b/src/generate/convert/handle.rs index 4aed55c8..adaf6224 100644 --- a/src/generate/convert/handle.rs +++ b/src/generate/convert/handle.rs @@ -22,7 +22,7 @@ pub fn convert_handle(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Conte } else { (None, None) }; - let assign_state = state.must_assign_to(var.as_deref()); + let assign_state = state.must_assign_to(var.as_deref(), expr_or_stmt.ty.clone()); Core::TryExcept { setup: var.map(|var| Box::from(Core::VarDef { var, ty, expr: None })), diff --git a/src/generate/convert/mod.rs b/src/generate/convert/mod.rs index d50b1f39..12a36965 100644 --- a/src/generate/convert/mod.rs +++ b/src/generate/convert/mod.rs @@ -3,6 +3,7 @@ use std::convert::TryFrom; use crate::{ASTTy, Context}; use crate::check::ast::NodeTy; use crate::check::context::clss::concrete_to_python; +use crate::check::name::Name; use crate::generate::ast::node::{Core, CoreOp}; use crate::generate::convert::builder::convert_builder; use crate::generate::convert::call::convert_call; @@ -14,6 +15,7 @@ use crate::generate::convert::handle::convert_handle; use crate::generate::convert::range_slice::convert_range_slice; use crate::generate::convert::state::{Imports, State}; use crate::generate::convert::ty::convert_ty; +use crate::generate::name::ToPy; use crate::generate::result::{GenResult, UnimplementedErr}; mod builder; @@ -33,7 +35,8 @@ pub fn convert_node(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context let must_assign_to = state.must_assign_to.clone(); let is_last_must_be_ret = state.is_last_must_be_ret; - let state = &state.must_assign_to(None).is_last_must_be_ret(false); + let old_state = state.clone(); + let state = &state.must_assign_to(None, None).is_last_must_be_ret(false); let core = match &ast.node { NodeTy::Import { from, import, alias } => Core::Import { @@ -98,8 +101,8 @@ pub fn convert_node(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context Core::Return { expr: Box::from(convert_node(expr, imp, state, ctx)?) } } - NodeTy::IfElse { .. } => convert_cntrl_flow(ast, imp, &state.is_last_must_be_ret(is_last_must_be_ret).must_assign_to(must_assign_to.as_ref()), ctx)?, - NodeTy::Match { .. } => convert_cntrl_flow(ast, imp, &state.expand_ty(false).is_last_must_be_ret(is_last_must_be_ret).must_assign_to(must_assign_to.as_ref()), ctx)?, + NodeTy::IfElse { .. } => convert_cntrl_flow(ast, imp, &old_state, ctx)?, + NodeTy::Match { .. } => convert_cntrl_flow(ast, imp, &old_state, ctx)?, NodeTy::While { .. } | NodeTy::For { .. } | NodeTy::Break | NodeTy::Continue => convert_cntrl_flow(ast, imp, state, ctx)?, NodeTy::Not { expr } => Core::Not { expr: Box::from(convert_node(expr, imp, state, ctx)?) }, @@ -261,8 +264,8 @@ pub fn convert_node(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context _ => Core::Empty, }; - let core = if let Some(assign_to) = must_assign_to { - append_assign(&core, &assign_to) + let core = if let Some((assign_to, name)) = must_assign_to { + append_assign(&core, &assign_to, &name, imp) } else { core }; let core = if is_last_must_be_ret { @@ -272,11 +275,11 @@ pub fn convert_node(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context Ok(core) } -fn append_assign(core: &Core, assign_to: &Core) -> Core { +fn append_assign(core: &Core, assign_to: &Core, name: &Option, imp: &mut Imports) -> Core { match &core { Core::Block { ref statements } => match statements.last() { Some(last) => { - let last = append_assign(last, assign_to); + let last = append_assign(last, assign_to, name, imp); let (mut statements, idx): (Vec, usize) = (statements.clone(), statements.len() - 1); statements[idx] = last; Core::Block { statements } @@ -285,36 +288,36 @@ fn append_assign(core: &Core, assign_to: &Core) -> Core { } Core::IfElse { cond, then, el } => Core::IfElse { cond: cond.clone(), - then: Box::from(append_assign(then, assign_to)), - el: Box::from(append_assign(el, assign_to)), + then: Box::from(append_assign(then, assign_to, name, imp)), + el: Box::from(append_assign(el, assign_to, name, imp)), }, Core::Match { expr, cases } => Core::Match { expr: expr.clone(), - cases: cases.iter().map(|c| append_assign(c, assign_to)).collect(), + cases: cases.iter().map(|c| append_assign(c, assign_to, name, imp)).collect(), }, Core::Case { expr, body } => Core::Case { expr: expr.clone(), - body: Box::from(append_assign(body, assign_to)), + body: Box::from(append_assign(body, assign_to, name, imp)), }, Core::TryExcept { setup, attempt, except } => Core::TryExcept { setup: setup.clone(), - attempt: Box::from(append_assign(attempt, assign_to)), - except: except.iter().map(|e| append_assign(e, assign_to)).collect(), + attempt: Box::from(append_assign(attempt, assign_to, name, imp)), + except: except.iter().map(|e| append_assign(e, assign_to, name, imp)).collect(), }, Core::ExceptId { id, class, body } => Core::ExceptId { id: id.clone(), class: class.clone(), - body: Box::from(append_assign(body, assign_to)), + body: Box::from(append_assign(body, assign_to, name, imp)), }, Core::Except { class, body } => Core::Except { class: class.clone(), - body: Box::from(append_assign(body, assign_to)), + body: Box::from(append_assign(body, assign_to, name, imp)), }, expr if skip_assign(expr) => core.clone(), - _ => Core::Assign { - left: Box::from(assign_to.clone()), - right: Box::from(core.clone()), - op: CoreOp::Assign, + _ => Core::VarDef { + var: Box::from(assign_to.clone()), + ty: name.clone().map(|name| Box::from(name.to_py(imp))), + expr: Option::from(Box::from(core.clone())) }, } } diff --git a/src/generate/convert/state.rs b/src/generate/convert/state.rs index f969a7ab..a7e31028 100644 --- a/src/generate/convert/state.rs +++ b/src/generate/convert/state.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use itertools::Itertools; +use crate::check::name::Name; use crate::generate::ast::node::Core; use crate::generate::GenArguments; @@ -17,7 +18,7 @@ pub struct State { pub annotate: bool, pub is_last_must_be_ret: bool, - pub must_assign_to: Option, + pub must_assign_to: Option<(Core, Option)>, pub is_remove_last_ret: bool, } @@ -68,8 +69,12 @@ impl State { State { def_as_fun_arg, ..self.clone() } } - pub fn must_assign_to(&self, assign_to: Option<&Core>) -> State { - State { must_assign_to: assign_to.cloned(), ..self.clone() } + pub fn must_assign_to(&self, must_assign_to: Option<&Core>, name: Option) -> State { + if let Some(must_assign_to) = must_assign_to { + State { must_assign_to: Some((must_assign_to.clone(), name)), ..self.clone() } + } else { + State { must_assign_to: None, ..self.clone() } + } } } diff --git a/src/parse/collection.rs b/src/parse/collection.rs index 195b40c6..ce25d8fa 100644 --- a/src/parse/collection.rs +++ b/src/parse/collection.rs @@ -198,7 +198,6 @@ mod test { } #[test] - #[ignore] fn list_expression() -> ParseResult<()> { let source = resource_content(true, &["collection"], "list.mamba"); parse(&source).map(|_| ()) diff --git a/tests/check/invalid/definition.rs b/tests/check/invalid/definition.rs index 133e7a47..4c461c36 100644 --- a/tests/check/invalid/definition.rs +++ b/tests/check/invalid/definition.rs @@ -56,7 +56,6 @@ fn assign_to_function_call() { } #[test] -#[ignore] // Ignore mutability for now fn assign_to_inner_non_mut() { let source = resource_content(false, &["type", "definition"], "assign_to_inner_non_mut.mamba"); check_all(&[*parse(&source).unwrap()]).unwrap_err(); @@ -69,7 +68,6 @@ fn assign_to_inner_non_mut2() { } #[test] -#[ignore] fn assign_to_inner_non_mut3() { let source = resource_content(false, &["type", "definition"], "assign_to_inner_non_mut3.mamba"); check_all(&[*parse(&source).unwrap()]).unwrap_err(); diff --git a/tests/resource/invalid/type/error/using_old_resource_in_with.mamba b/tests/resource/invalid/type/error/using_old_resource_in_with.mamba index 14ca2b96..e55f5c8e 100644 --- a/tests/resource/invalid/type/error/using_old_resource_in_with.mamba +++ b/tests/resource/invalid/type/error/using_old_resource_in_with.mamba @@ -1,4 +1,4 @@ def old := 10 with old as new do - print(old + 10) \ No newline at end of file + print(old + 10) diff --git a/tests/resource/invalid/type/error/with_not_expression.mamba b/tests/resource/invalid/type/error/with_not_expression.mamba index b3b9d650..f704f6a6 100644 --- a/tests/resource/invalid/type/error/with_not_expression.mamba +++ b/tests/resource/invalid/type/error/with_not_expression.mamba @@ -1,4 +1,4 @@ def not_an_expression() => print("not an expression") with not_an_expression() as my_expression do - print("not an expression!") \ No newline at end of file + print("not an expression!") diff --git a/tests/resource/valid/class/same_var_different_type.mamba b/tests/resource/valid/class/same_var_different_type.mamba new file mode 100644 index 00000000..d3e4c310 --- /dev/null +++ b/tests/resource/valid/class/same_var_different_type.mamba @@ -0,0 +1,7 @@ +class C1 + def a: Str := "str" + def f(self) -> Str => self.a + +class C2 + def a: Int := 10 + def f(self) -> Str => self.a diff --git a/tests/resource/valid/class/same_var_different_type_check.py b/tests/resource/valid/class/same_var_different_type_check.py new file mode 100644 index 00000000..a41e2cd9 --- /dev/null +++ b/tests/resource/valid/class/same_var_different_type_check.py @@ -0,0 +1,9 @@ +class C1: + a: str = "str" + def f(self) -> str: + return self.a + +class C2: + a: int = 10 + def f(self) -> str: + return self.a diff --git a/tests/resource/valid/class/types_check.py b/tests/resource/valid/class/types_check.py index e10bcb1d..df39fe0d 100644 --- a/tests/resource/valid/class/types_check.py +++ b/tests/resource/valid/class/types_check.py @@ -25,7 +25,6 @@ def __init__(self): def higher_order(self) -> int: pass -# some class class MyClass(MyType, MyInterface): required_field: int = 100 private_field: int = 20 diff --git a/tests/resource/valid/control_flow/assign_if_check.py b/tests/resource/valid/control_flow/assign_if_check.py index 65b8d3e8..74c9f041 100644 --- a/tests/resource/valid/control_flow/assign_if_check.py +++ b/tests/resource/valid/control_flow/assign_if_check.py @@ -1,6 +1,6 @@ if True: print("then") - my_var = 10 + my_var: int = 10 else: print("else") - my_var = 20 + my_var: int = 20 diff --git a/tests/resource/valid/control_flow/double_assign_if_check.py b/tests/resource/valid/control_flow/double_assign_if_check.py index 0bf318a0..93525e2d 100644 --- a/tests/resource/valid/control_flow/double_assign_if_check.py +++ b/tests/resource/valid/control_flow/double_assign_if_check.py @@ -1,6 +1,6 @@ other: int = 20 if True: other = 30 - my_variable = 20 + my_variable: int = 20 else: - my_variable = 10 + my_variable: int = 10 diff --git a/tests/resource/valid/control_flow/handle_in_if_check.py b/tests/resource/valid/control_flow/handle_in_if_check.py index 6e9d2b95..c5fc0116 100644 --- a/tests/resource/valid/control_flow/handle_in_if_check.py +++ b/tests/resource/valid/control_flow/handle_in_if_check.py @@ -3,11 +3,11 @@ def f() -> int: if True: try: - x = f() + x: int = f() except Exception as err: - x = 3 + x: int = 3 else: try: - x = f() + x: int = f() except Exception as err: - x = 3 + x: int = 3 diff --git a/tests/resource/valid/control_flow/shadow_in_if_arms.mamba b/tests/resource/valid/control_flow/shadow_in_if_arms.mamba new file mode 100644 index 00000000..abacb23b --- /dev/null +++ b/tests/resource/valid/control_flow/shadow_in_if_arms.mamba @@ -0,0 +1,11 @@ +class MyClass1 + def f1(self) => print("1") +class MyClass2 + def f2(self) => print("2") + +if True then + def x := MyClass1() + x.f1() +else + def x := MyClass2() + x.f2() diff --git a/tests/resource/valid/control_flow/shadow_in_if_arms_check.py b/tests/resource/valid/control_flow/shadow_in_if_arms_check.py new file mode 100644 index 00000000..0011293b --- /dev/null +++ b/tests/resource/valid/control_flow/shadow_in_if_arms_check.py @@ -0,0 +1,14 @@ +class MyClass1: + def f1(self): + print("1") + +class MyClass2: + def f2(self): + print("2") + +if True: + x: MyClass1 = MyClass1() + x.f1() +else: + x: MyClass2 = MyClass2() + x.f2() diff --git a/tests/resource/valid/control_flow/shadow_in_if_arms_else.mamba b/tests/resource/valid/control_flow/shadow_in_if_arms_else.mamba new file mode 100644 index 00000000..7e2e5a70 --- /dev/null +++ b/tests/resource/valid/control_flow/shadow_in_if_arms_else.mamba @@ -0,0 +1,11 @@ +class MyClass1 + def f1(self) => pass +class MyClass2 + def f2(self) => pass + +def x := MyClass2() +if True then + x.f2() +else + def x := MyClass1() + x.f1() diff --git a/tests/resource/valid/control_flow/shadow_in_if_arms_else_check.py b/tests/resource/valid/control_flow/shadow_in_if_arms_else_check.py new file mode 100644 index 00000000..73f6bba6 --- /dev/null +++ b/tests/resource/valid/control_flow/shadow_in_if_arms_else_check.py @@ -0,0 +1,14 @@ +class MyClass1: + def f1(self): + pass + +class MyClass2: + def f2(self): + pass + +x: MyClass2 = MyClass2() +if True: + x.f2() +else: + x: MyClass1 = MyClass1() + x.f1() diff --git a/tests/resource/valid/control_flow/shadow_in_if_arms_then.mamba b/tests/resource/valid/control_flow/shadow_in_if_arms_then.mamba new file mode 100644 index 00000000..1cf3167a --- /dev/null +++ b/tests/resource/valid/control_flow/shadow_in_if_arms_then.mamba @@ -0,0 +1,11 @@ +class MyClass1 + def f1(self) => pass +class MyClass2 + def f2(self) => pass + +def x := MyClass2() +if True then + def x := MyClass1() + x.f1() +else + x.f2() diff --git a/tests/resource/valid/control_flow/shadow_in_if_arms_then_check.py b/tests/resource/valid/control_flow/shadow_in_if_arms_then_check.py new file mode 100644 index 00000000..20d9a45e --- /dev/null +++ b/tests/resource/valid/control_flow/shadow_in_if_arms_then_check.py @@ -0,0 +1,14 @@ +class MyClass1: + def f1(self): + pass + +class MyClass2: + def f2(self): + pass + +x: MyClass2 = MyClass2() +if True: + x: MyClass1 = MyClass1() + x.f1() +else: + x.f2() diff --git a/tests/resource/valid/definition/assign_with_if_different_types.mamba b/tests/resource/valid/definition/assign_with_if_different_types.mamba new file mode 100644 index 00000000..d15cee49 --- /dev/null +++ b/tests/resource/valid/definition/assign_with_if_different_types.mamba @@ -0,0 +1,6 @@ +class MyClass + def __str__(self) -> Str => "M" + +def a := if True then 20 else MyClass() + +print(a) diff --git a/tests/resource/valid/definition/assign_with_if_different_types_check.py b/tests/resource/valid/definition/assign_with_if_different_types_check.py new file mode 100644 index 00000000..a6b84e4d --- /dev/null +++ b/tests/resource/valid/definition/assign_with_if_different_types_check.py @@ -0,0 +1,9 @@ +from typing import Union + +class MyClass: + def __str__(self) -> str: + return "M" + +a: Union[int, MyClass] = 20 if True else MyClass() + +print(a) diff --git a/tests/resource/valid/definition/assign_with_match.mamba b/tests/resource/valid/definition/assign_with_match.mamba index d1b94907..bc141f88 100644 --- a/tests/resource/valid/definition/assign_with_match.mamba +++ b/tests/resource/valid/definition/assign_with_match.mamba @@ -1,4 +1,4 @@ -def a:= match 40 +def a := match 40 2 => 3 4 => 30 _ => 300 diff --git a/tests/resource/valid/definition/assign_with_match_different_types.mamba b/tests/resource/valid/definition/assign_with_match_different_types.mamba new file mode 100644 index 00000000..f3eacf01 --- /dev/null +++ b/tests/resource/valid/definition/assign_with_match_different_types.mamba @@ -0,0 +1,8 @@ +class MyClass +class MyClass1 +class MyClass2 + +def a:= match 40 + 2 => MyClass() + 4 => MyClass1() + _ => MyClass2() diff --git a/tests/resource/valid/definition/assign_with_match_different_types_check.py b/tests/resource/valid/definition/assign_with_match_different_types_check.py new file mode 100644 index 00000000..a86231e9 --- /dev/null +++ b/tests/resource/valid/definition/assign_with_match_different_types_check.py @@ -0,0 +1,17 @@ +from typing import Union +class MyClass: + pass + +class MyClass1: + pass + +class MyClass2: + pass + +match 40: + case 2: + a: Union[MyClass, MyClass1, MyClass2] = MyClass() + case 4: + a: Union[MyClass, MyClass1, MyClass2] = MyClass1() + case _: + a: Union[MyClass, MyClass1, MyClass2] = MyClass2() diff --git a/tests/resource/valid/definition/assign_with_match_type_annotation.mamba b/tests/resource/valid/definition/assign_with_match_type_annotation.mamba new file mode 100644 index 00000000..5b962175 --- /dev/null +++ b/tests/resource/valid/definition/assign_with_match_type_annotation.mamba @@ -0,0 +1,4 @@ +def a: Int := match 40 + 2 => 3 + 4 => 30 + _ => 300 diff --git a/tests/resource/valid/definition/assign_with_match_type_annotation_check.py b/tests/resource/valid/definition/assign_with_match_type_annotation_check.py new file mode 100644 index 00000000..147cb4c9 --- /dev/null +++ b/tests/resource/valid/definition/assign_with_match_type_annotation_check.py @@ -0,0 +1,7 @@ +match 40: + case 2: + a: int = 3 + case 4: + a: int = 30 + case _: + a: int = 300 diff --git a/tests/resource/valid/definition/assign_with_nested_if_check.py b/tests/resource/valid/definition/assign_with_nested_if_check.py index da65387a..c448175c 100644 --- a/tests/resource/valid/definition/assign_with_nested_if_check.py +++ b/tests/resource/valid/definition/assign_with_nested_if_check.py @@ -2,8 +2,8 @@ if x > 10: if x > 30: print("string") - a = x + 1 + a: int = x + 1 else: - a = x - 1 + a: int = x - 1 else: - a = x + a: int = x diff --git a/tests/resource/valid/function/definition_check.py b/tests/resource/valid/function/definition_check.py index 35342a4d..0c8d8569 100644 --- a/tests/resource/valid/function/definition_check.py +++ b/tests/resource/valid/function/definition_check.py @@ -7,7 +7,10 @@ def fun_a() -> Optional[int]: if False or True: print("world") a = None or 11 - return 10 if True else None + if True: + return 10 + else: + return None def fun_b(b: int): print(b) diff --git a/tests/system/valid/class.rs b/tests/system/valid/class.rs index 09c6347e..482900a2 100644 --- a/tests/system/valid/class.rs +++ b/tests/system/valid/class.rs @@ -6,7 +6,6 @@ fn assign_to_nullable_field() -> OutTestRet { } #[test] -#[ignore] // Generics have not properly been implemented fn generics() -> OutTestRet { test_directory(true, &["class"], &["class", "target"], "generics") } @@ -41,6 +40,11 @@ fn generic_unknown_type_unused() -> OutTestRet { test_directory(true, &["class"], &["class", "target"], "generic_unknown_type_unused") } +#[test] +fn same_var_different_type() -> OutTestRet { + test_directory(true, &["class"], &["class", "target"], "same_var_different_type") +} + #[test] fn doc_strings() -> OutTestRet { test_directory(true, &["class"], &["class", "target"], "doc_strings") diff --git a/tests/system/valid/control_flow.rs b/tests/system/valid/control_flow.rs index e0601cfa..87c6ad74 100644 --- a/tests/system/valid/control_flow.rs +++ b/tests/system/valid/control_flow.rs @@ -57,7 +57,6 @@ fn if_in_for_loop() -> OutTestRet { } #[test] -#[ignore] // not sure if the check stage should pass as of yet fn if_two_types() -> OutTestRet { test_directory(true, &["control_flow"], &["control_flow", "target"], "if_two_types") } @@ -72,6 +71,21 @@ fn matches_in_if() -> OutTestRet { test_directory(true, &["control_flow"], &["control_flow", "target"], "matches_in_if") } +#[test] +fn shadow_in_if_arms() -> OutTestRet { + test_directory(true, &["control_flow"], &["control_flow", "target"], "shadow_in_if_arms") +} + +#[test] +fn shadow_in_if_arms_then() -> OutTestRet { + test_directory(true, &["control_flow"], &["control_flow", "target"], "shadow_in_if_arms_then") +} + +#[test] +fn shadow_in_if_arms_else() -> OutTestRet { + test_directory(true, &["control_flow"], &["control_flow", "target"], "shadow_in_if_arms_else") +} + #[test] fn while_ast_verify() -> OutTestRet { test_directory(true, &["control_flow"], &["control_flow", "target"], "while") diff --git a/tests/system/valid/definition.rs b/tests/system/valid/definition.rs index a922e153..b59c4727 100644 --- a/tests/system/valid/definition.rs +++ b/tests/system/valid/definition.rs @@ -20,11 +20,26 @@ fn assign_with_if() -> OutTestRet { test_directory(true, &["definition"], &["definition", "target"], "assign_with_if") } +#[test] +fn assign_with_if_different_types() -> OutTestRet { + test_directory(true, &["definition"], &["definition", "target"], "assign_with_if_different_types") +} + #[test] fn assign_with_match() -> OutTestRet { test_directory(true, &["definition"], &["definition", "target"], "assign_with_match") } +#[test] +fn assign_with_match_different_types() -> OutTestRet { + test_directory(true, &["definition"], &["definition", "target"], "assign_with_match_different_types") +} + +#[test] +fn assign_with_match_type_annotation() -> OutTestRet { + test_directory(true, &["definition"], &["definition", "target"], "assign_with_match_type_annotation") +} + #[test] fn assign_with_nested_if() -> OutTestRet { test_directory(true, &["definition"], &["definition", "target"], "assign_with_nested_if")