Add other shadowing cases
This commit is contained in:
parent
fc948f0029
commit
dba0e11ba7
|
@ -583,6 +583,63 @@ pub fn erase_opaque_type_operations(
|
|||
}
|
||||
}
|
||||
|
||||
/// Determine whether this air_tree node introduces any shadowing over `potential_matches`
|
||||
pub fn find_introduced_variables(air_tree: &AirTree) -> Vec<String> {
|
||||
match air_tree {
|
||||
AirTree::Statement { statement: AirStatement::Let { name, .. }, .. } => vec![name.clone()],
|
||||
AirTree::Statement { statement: AirStatement::TupleGuard { indices, .. }, .. } |
|
||||
AirTree::Expression(AirExpression::TupleClause { indices, .. }) => {
|
||||
indices.iter().map(|(_, name)| name).cloned().collect()
|
||||
},
|
||||
AirTree::Expression(AirExpression::Fn { params, .. }) => {
|
||||
params.iter().map(|name| name).cloned().collect()
|
||||
},
|
||||
AirTree::Statement { statement: AirStatement::ListAccessor { names, .. }, ..} => {
|
||||
names.clone()
|
||||
}
|
||||
AirTree::Statement { statement: AirStatement::ListExpose { tail, tail_head_names, .. }, .. } => {
|
||||
let mut ret = vec![];
|
||||
if let Some((_, head)) = tail {
|
||||
ret.push(head.clone())
|
||||
}
|
||||
|
||||
for name in tail_head_names.iter().map(|(_, head)| head) {
|
||||
ret.push(name.clone());
|
||||
}
|
||||
ret
|
||||
},
|
||||
AirTree::Statement { statement: AirStatement::TupleAccessor { names, .. }, .. } => {
|
||||
names.clone()
|
||||
},
|
||||
AirTree::Statement { statement: AirStatement::FieldsExpose { indices, .. }, ..} => {
|
||||
indices.iter().map(|(_, name, _)| name).cloned().collect()
|
||||
}
|
||||
_ => vec![]
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether a function is recursive, and if so, get the arguments
|
||||
pub fn is_recursive_function_call<'a>(air_tree: &'a AirTree, func_key: &FunctionAccessKey, variant: &String) -> (bool, Option<&'a Vec<AirTree>>) {
|
||||
if let AirTree::Expression(AirExpression::Call { func, args, .. }) = air_tree {
|
||||
if let AirTree::Expression(AirExpression::Var {
|
||||
constructor:
|
||||
ValueConstructor {
|
||||
variant: ValueConstructorVariant::ModuleFn { name, module, .. },
|
||||
..
|
||||
},
|
||||
variant_name,
|
||||
..
|
||||
}) = func.as_ref() {
|
||||
if name == &func_key.function_name
|
||||
&& module == &func_key.module_name
|
||||
&& variant == variant_name {
|
||||
return (true, Some(args))
|
||||
}
|
||||
}
|
||||
}
|
||||
return (false, None)
|
||||
}
|
||||
|
||||
pub fn identify_recursive_static_params(
|
||||
air_tree: &mut AirTree,
|
||||
tree_path: &TreePath,
|
||||
|
@ -592,54 +649,38 @@ pub fn identify_recursive_static_params(
|
|||
shadowed_parameters: &mut HashMap<String, TreePath>,
|
||||
potential_recursive_statics: &mut Vec<String>
|
||||
) {
|
||||
match air_tree {
|
||||
AirTree::Statement { statement: AirStatement::Let { name, .. }, .. } => {
|
||||
if potential_recursive_statics.contains(name) {
|
||||
shadowed_parameters.insert(name.clone(), tree_path.clone());
|
||||
}
|
||||
},
|
||||
AirTree::Expression(AirExpression::Call { func, args, .. }) => {
|
||||
if let AirTree::Expression(AirExpression::Var {
|
||||
constructor:
|
||||
ValueConstructor {
|
||||
variant: ValueConstructorVariant::ModuleFn { name, module, .. },
|
||||
..
|
||||
},
|
||||
variant_name,
|
||||
..
|
||||
}) = func.as_ref() {
|
||||
if name == &func_key.function_name
|
||||
&& module == &func_key.module_name
|
||||
&& variant == variant_name
|
||||
{
|
||||
for (param, arg) in func_params.iter().zip(args) {
|
||||
if let Some((idx, _)) = potential_recursive_statics.iter().find_position(|&p| p == param) {
|
||||
// Check if we pass something different in this recursive call site
|
||||
// by different, we mean
|
||||
// - a variable that is bound to a different name
|
||||
// - a variable with the same name, but that was shadowed in an ancestor scope
|
||||
// - any other type of expression
|
||||
let param_is_different = match arg {
|
||||
AirTree::Expression(AirExpression::Var { name, .. }) => {
|
||||
// "shadowed in an ancestor scope" means "the definition scope is a prefix of our scope"
|
||||
name != param || if let Some(p) = shadowed_parameters.get(param) {
|
||||
p.common_ancestor(tree_path) == *p
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
_ => true
|
||||
};
|
||||
// If so, then we disqualify this parameter from being a recursive static parameter
|
||||
if param_is_different {
|
||||
potential_recursive_statics.remove(idx);
|
||||
}
|
||||
// Find whether any of the potential recursive statics get shadowed (because even if we pass in the same referenced name, it might not be static)
|
||||
for introduced_variable in find_introduced_variables(air_tree) {
|
||||
if potential_recursive_statics.contains(&introduced_variable) {
|
||||
shadowed_parameters.insert(introduced_variable, tree_path.clone());
|
||||
}
|
||||
}
|
||||
// Otherwise, if this is a recursive call site, disqualify anything that is different (or the same, but shadowed)
|
||||
if let (true, Some(args)) = is_recursive_function_call(air_tree, func_key, variant) {
|
||||
for (param, arg) in func_params.iter().zip(args) {
|
||||
if let Some((idx, _)) = potential_recursive_statics.iter().find_position(|&p| p == param) {
|
||||
// Check if we pass something different in this recursive call site
|
||||
// by different, we mean
|
||||
// - a variable that is bound to a different name
|
||||
// - a variable with the same name, but that was shadowed in an ancestor scope
|
||||
// - any other type of expression
|
||||
let param_is_different = match arg {
|
||||
AirTree::Expression(AirExpression::Var { name, .. }) => {
|
||||
// "shadowed in an ancestor scope" means "the definition scope is a prefix of our scope"
|
||||
name != param || if let Some(p) = shadowed_parameters.get(param) {
|
||||
p.common_ancestor(tree_path) == *p
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => true
|
||||
};
|
||||
// If so, then we disqualify this parameter from being a recursive static parameter
|
||||
if param_is_different {
|
||||
potential_recursive_statics.remove(idx);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue