Merge remote-tracking branch 'origin/trunk' into builtins-in-roc-delayed-alias

This commit is contained in:
Folkert 2022-03-18 21:25:52 +01:00
commit 4e1197165b
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
181 changed files with 9495 additions and 2273 deletions

View file

@ -1,12 +1,15 @@
use crate::env::Env;
use crate::scope::Scope;
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
use roc_error_macros::todo_abilities;
use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_parse::ast::{AssignedField, Pattern, Tag, TypeAnnotation, TypeHeader};
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{Alias, AliasKind, LambdaSet, Problem, RecordField, Type};
use roc_types::types::{
Alias, AliasCommon, AliasKind, LambdaSet, Problem, RecordField, Type, TypeExtension,
};
#[derive(Clone, Debug, PartialEq)]
pub struct Annotation {
@ -28,25 +31,27 @@ pub struct IntroducedVariables {
// But then between annotations, the same name can occur multiple times,
// but a variable can only have one name. Therefore
// `ftv : SendMap<Variable, Lowercase>`.
pub wildcards: Vec<Variable>,
pub wildcards: Vec<Loc<Variable>>,
pub lambda_sets: Vec<Variable>,
pub inferred: Vec<Variable>,
pub var_by_name: SendMap<Lowercase, Variable>,
pub inferred: Vec<Loc<Variable>>,
// NB: A mapping of a -> Loc<v1> in this map has the region of the first-seen var, but there
// may be multiple occurrences of it!
pub var_by_name: SendMap<Lowercase, Loc<Variable>>,
pub name_by_var: SendMap<Variable, Lowercase>,
pub host_exposed_aliases: MutMap<Symbol, Variable>,
}
impl IntroducedVariables {
pub fn insert_named(&mut self, name: Lowercase, var: Variable) {
pub fn insert_named(&mut self, name: Lowercase, var: Loc<Variable>) {
self.var_by_name.insert(name.clone(), var);
self.name_by_var.insert(var, name);
self.name_by_var.insert(var.value, name);
}
pub fn insert_wildcard(&mut self, var: Variable) {
pub fn insert_wildcard(&mut self, var: Loc<Variable>) {
self.wildcards.push(var);
}
pub fn insert_inferred(&mut self, var: Variable) {
pub fn insert_inferred(&mut self, var: Loc<Variable>) {
self.inferred.push(var);
}
@ -69,7 +74,7 @@ impl IntroducedVariables {
}
pub fn var_by_name(&self, name: &Lowercase) -> Option<&Variable> {
self.var_by_name.get(name)
self.var_by_name.get(name).map(|v| &v.value)
}
pub fn name_by_var(&self, var: Variable) -> Option<&Lowercase> {
@ -242,6 +247,7 @@ pub fn find_type_def_symbols(
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
stack.push(inner);
}
Where(..) => todo_abilities!(),
Inferred | Wildcard | Malformed(_) => {}
}
}
@ -284,7 +290,7 @@ fn can_annotation_help(
let ret = can_annotation_help(
env,
&return_type.value,
region,
return_type.region,
scope,
var_store,
introduced_variables,
@ -312,7 +318,7 @@ fn can_annotation_help(
let arg_ann = can_annotation_help(
env,
&arg.value,
region,
arg.region,
scope,
var_store,
introduced_variables,
@ -337,22 +343,43 @@ fn can_annotation_help(
return error;
}
let (type_arguments, lambda_set_variables, actual) =
instantiate_and_freshen_alias_type(
var_store,
introduced_variables,
&alias.type_variables,
args,
&alias.lambda_set_variables,
alias.typ.clone(),
);
// For now, aliases of function types cannot be delayed.
// This is a limitation of the current implementation,
// and this totally should be possible in the future.
let is_import = !symbol.is_builtin() && (env.home != symbol.module_id());
let is_structural = alias.kind == AliasKind::Structural;
if !is_import && is_structural && alias.lambda_set_variables.is_empty() {
let mut type_var_to_arg = Vec::new();
Type::Alias {
symbol,
type_arguments,
lambda_set_variables,
actual: Box::new(actual),
kind: alias.kind,
for (loc_var, arg_ann) in alias.type_variables.iter().zip(args) {
let name = loc_var.value.0.clone();
type_var_to_arg.push((name, arg_ann));
}
Type::DelayedAlias(AliasCommon {
symbol,
type_arguments: type_var_to_arg,
lambda_set_variables: alias.lambda_set_variables.clone(),
})
} else {
let (type_arguments, lambda_set_variables, actual) =
instantiate_and_freshen_alias_type(
var_store,
introduced_variables,
&alias.type_variables,
args,
&alias.lambda_set_variables,
alias.typ.clone(),
);
Type::Alias {
symbol,
type_arguments,
lambda_set_variables,
actual: Box::new(actual),
kind: alias.kind,
}
}
}
None => Type::Apply(symbol, args, region),
@ -366,7 +393,7 @@ fn can_annotation_help(
None => {
let var = var_store.fresh();
introduced_variables.insert_named(name, var);
introduced_variables.insert_named(name, Loc::at(region, var));
Type::Variable(var)
}
@ -430,7 +457,8 @@ fn can_annotation_help(
} else {
let var = var_store.fresh();
introduced_variables.insert_named(var_name.clone(), var);
introduced_variables
.insert_named(var_name.clone(), Loc::at(loc_var.region, var));
vars.push((var_name.clone(), Type::Variable(var)));
lowercase_vars.push(Loc::at(loc_var.region, (var_name, var)));
@ -537,7 +565,7 @@ fn can_annotation_help(
// just `a` does not mean the same as `{}a`, so even
// if there are no fields, still make this a `Record`,
// not an EmptyRec
Type::Record(Default::default(), Box::new(ext_type))
Type::Record(Default::default(), TypeExtension::from_type(ext_type))
}
None => Type::EmptyRec,
@ -554,7 +582,7 @@ fn can_annotation_help(
references,
);
Type::Record(field_types, Box::new(ext_type))
Type::Record(field_types, TypeExtension::from_type(ext_type))
}
}
TagUnion { tags, ext, .. } => {
@ -575,7 +603,7 @@ fn can_annotation_help(
// just `a` does not mean the same as `{}a`, so even
// if there are no fields, still make this a `Record`,
// not an EmptyRec
Type::TagUnion(Default::default(), Box::new(ext_type))
Type::TagUnion(Default::default(), TypeExtension::from_type(ext_type))
}
None => Type::EmptyTagUnion,
@ -597,7 +625,7 @@ fn can_annotation_help(
// in theory we save a lot of time by sorting once here
insertion_sort_by(&mut tag_types, |a, b| a.0.cmp(&b.0));
Type::TagUnion(tag_types, Box::new(ext_type))
Type::TagUnion(tag_types, TypeExtension::from_type(ext_type))
}
}
SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_annotation_help(
@ -613,7 +641,7 @@ fn can_annotation_help(
Wildcard => {
let var = var_store.fresh();
introduced_variables.insert_wildcard(var);
introduced_variables.insert_wildcard(Loc::at(region, var));
Type::Variable(var)
}
@ -622,16 +650,17 @@ fn can_annotation_help(
// make a fresh unconstrained variable, and let the type solver fill it in for us 🤠
let var = var_store.fresh();
introduced_variables.insert_inferred(var);
introduced_variables.insert_inferred(Loc::at(region, var));
Type::Variable(var)
}
Where(..) => todo_abilities!(),
Malformed(string) => {
malformed(env, region, string);
let var = var_store.fresh();
introduced_variables.insert_wildcard(var);
introduced_variables.insert_wildcard(Loc::at(region, var));
Type::Variable(var)
}
@ -682,7 +711,7 @@ fn can_extension_type<'a>(
local_aliases,
references,
);
if valid_extension_type(ext_type.shallow_dealias()) {
if valid_extension_type(shallow_dealias_with_scope(scope, &ext_type)) {
ext_type
} else {
// Report an error but mark the extension variable to be inferred
@ -697,7 +726,7 @@ fn can_extension_type<'a>(
let var = var_store.fresh();
introduced_variables.insert_inferred(var);
introduced_variables.insert_inferred(Loc::at_zero(var));
Type::Variable(var)
}
@ -706,6 +735,29 @@ fn can_extension_type<'a>(
}
}
/// a shallow dealias, continue until the first constructor is not an alias.
fn shallow_dealias_with_scope<'a>(scope: &'a mut Scope, typ: &'a Type) -> &'a Type {
let mut result = typ;
loop {
match result {
Type::Alias { actual, .. } => {
// another loop
result = actual;
}
Type::DelayedAlias(AliasCommon { symbol, .. }) => match scope.lookup_alias(*symbol) {
None => unreachable!(),
Some(alias) => {
result = &alias.typ;
}
},
_ => break,
}
}
result
}
pub fn instantiate_and_freshen_alias_type(
var_store: &mut VarStore,
introduced_variables: &mut IntroducedVariables,
@ -866,7 +918,10 @@ fn can_assigned_fields<'a>(
Type::Variable(*var)
} else {
let field_var = var_store.fresh();
introduced_variables.insert_named(field_name.clone(), field_var);
introduced_variables.insert_named(
field_name.clone(),
Loc::at(loc_field_name.region, field_var),
);
Type::Variable(field_var)
}
};

View file

@ -247,6 +247,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
NUM_TO_U64_CHECKED => num_to_u64_checked,
NUM_TO_U128 => num_to_u128,
NUM_TO_U128_CHECKED => num_to_u128_checked,
NUM_TO_NAT => num_to_nat,
NUM_TO_NAT_CHECKED => num_to_nat_checked,
NUM_TO_STR => num_to_str,
RESULT_MAP => result_map,
RESULT_MAP_ERR => result_map_err,
@ -254,6 +256,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
RESULT_WITH_DEFAULT => result_with_default,
RESULT_IS_OK => result_is_ok,
RESULT_IS_ERR => result_is_err,
BOX_BOX_FUNCTION => box_box,
BOX_UNBOX => box_unbox,
}
}
@ -455,6 +459,12 @@ fn num_to_u128(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
}
// Num.toNat : Int * -> Nat
fn num_to_nat(symbol: Symbol, var_store: &mut VarStore) -> Def {
// Defer to IntCast
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
}
fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) -> Def {
let bool_var = var_store.fresh();
let num_var_1 = var_store.fresh();
@ -561,6 +571,7 @@ num_to_checked! {
num_to_u32_checked
num_to_u64_checked
num_to_u128_checked
num_to_nat_checked
}
// Num.toStr : Num a -> Str
@ -5314,6 +5325,16 @@ fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level
)
}
/// Box.box : a -> Box a
fn box_box(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::BoxExpr, var_store)
}
/// Box.unbox : Box a -> a
fn box_unbox(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::UnboxExpr, var_store)
}
#[inline(always)]
fn defn_help(
fn_name: Symbol,

View file

@ -1,5 +1,5 @@
use crate::expected::{Expected, PExpected};
use roc_collections::soa::{Index, Slice};
use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_module::ident::TagName;
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
@ -120,14 +120,27 @@ impl Constraints {
pub const PCATEGORY_CHARACTER: Index<PatternCategory> = Index::new(10);
#[inline(always)]
pub fn push_type(&mut self, typ: Type) -> Index<Type> {
pub fn push_type(&mut self, typ: Type) -> EitherIndex<Type, Variable> {
match typ {
Type::EmptyRec => Self::EMPTY_RECORD,
Type::EmptyTagUnion => Self::EMPTY_TAG_UNION,
other => Index::push_new(&mut self.types, other),
Type::EmptyRec => EitherIndex::from_left(Self::EMPTY_RECORD),
Type::EmptyTagUnion => EitherIndex::from_left(Self::EMPTY_TAG_UNION),
Type::Variable(var) => Self::push_type_variable(var),
other => {
let index: Index<Type> = Index::push_new(&mut self.types, other);
EitherIndex::from_left(index)
}
}
}
#[inline(always)]
const fn push_type_variable(var: Variable) -> EitherIndex<Type, Variable> {
// that's right, we use the variable's integer value as the index
// that way, we don't need to push anything onto a vector
let index: Index<Variable> = Index::new(var.index());
EitherIndex::from_right(index)
}
#[inline(always)]
pub fn push_expected_type(&mut self, expected: Expected<Type>) -> Index<Expected<Type>> {
Index::push_new(&mut self.expectations, expected)
@ -180,13 +193,56 @@ impl Constraints {
category: Category,
region: Region,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let type_index = self.push_type(typ);
let expected_index = Index::push_new(&mut self.expectations, expected);
let category_index = Self::push_category(self, category);
Constraint::Eq(type_index, expected_index, category_index, region)
}
#[inline(always)]
pub fn equal_types_var(
&mut self,
var: Variable,
expected: Expected<Type>,
category: Category,
region: Region,
) -> Constraint {
let type_index = Self::push_type_variable(var);
let expected_index = Index::push_new(&mut self.expectations, expected);
let category_index = Self::push_category(self, category);
Constraint::Eq(type_index, expected_index, category_index, region)
}
#[inline(always)]
pub fn equal_types_with_storage(
&mut self,
typ: Type,
expected: Expected<Type>,
category: Category,
region: Region,
storage_var: Variable,
) -> Constraint {
let type_index = self.push_type(typ);
let expected_index = Index::push_new(&mut self.expectations, expected);
let category_index = Self::push_category(self, category);
let equal = Constraint::Eq(type_index, expected_index, category_index, region);
let storage_type_index = Self::push_type_variable(storage_var);
let storage_category = Category::Storage(std::file!(), std::line!());
let storage_category_index = Self::push_category(self, storage_category);
let storage = Constraint::Eq(
storage_type_index,
expected_index,
storage_category_index,
region,
);
self.and_constraint([equal, storage])
}
pub fn equal_pattern_types(
&mut self,
typ: Type,
@ -194,7 +250,7 @@ impl Constraints {
category: PatternCategory,
region: Region,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let type_index = self.push_type(typ);
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
let category_index = Self::push_pattern_category(self, category);
@ -208,7 +264,7 @@ impl Constraints {
category: PatternCategory,
region: Region,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let type_index = self.push_type(typ);
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
let category_index = Index::push_new(&mut self.pattern_categories, category);
@ -216,7 +272,7 @@ impl Constraints {
}
pub fn is_open_type(&mut self, typ: Type) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let type_index = self.push_type(typ);
Constraint::IsOpenType(type_index)
}
@ -309,7 +365,7 @@ impl Constraints {
let let_index = Index::new(self.let_constraints.len() as _);
self.let_constraints.push(let_contraint);
Constraint::Let(let_index)
Constraint::Let(let_index, Slice::default())
}
#[inline(always)]
@ -335,7 +391,7 @@ impl Constraints {
let let_index = Index::new(self.let_constraints.len() as _);
self.let_constraints.push(let_contraint);
Constraint::Let(let_index)
Constraint::Let(let_index, Slice::default())
}
#[inline(always)]
@ -353,6 +409,7 @@ impl Constraints {
I3: IntoIterator<Item = (Symbol, Loc<Type>)>,
I3::IntoIter: ExactSizeIterator,
{
// defs and ret constraint are stored consequtively, so we only need to store one index
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
self.constraints.push(defs_constraint);
@ -368,7 +425,55 @@ impl Constraints {
let let_index = Index::new(self.let_constraints.len() as _);
self.let_constraints.push(let_contraint);
Constraint::Let(let_index)
Constraint::Let(let_index, Slice::default())
}
/// A variant of `Let` used specifically for imports. When importing types from another module,
/// we use a StorageSubs to store the data, and copy over the relevant
/// variables/content/flattype/tagname etc.
///
/// The general idea is to let-generalize the imorted types in the target module.
/// More concretely, we need to simulate what `type_to_var` (solve.rs) does to a `Type`.
/// While the copying puts all the data the right place, it misses that `type_to_var` puts
/// the variables that it creates (to store the nodes of a Type in Subs) in the pool of the
/// current rank (so they can be generalized).
///
/// So, during copying of an import (`copy_import_to`, subs.rs) we track the variables that
/// we need to put into the pool (simulating what `type_to_var` would do). Those variables
/// then need to find their way to the pool, and a convenient approach turned out to be to
/// tag them onto the `Let` that we used to add the imported values.
#[inline(always)]
pub fn let_import_constraint<I1, I2>(
&mut self,
rigid_vars: I1,
def_types: I2,
module_constraint: Constraint,
pool_variables: &[Variable],
) -> Constraint
where
I1: IntoIterator<Item = Variable>,
I2: IntoIterator<Item = (Symbol, Loc<Type>)>,
I2::IntoIter: ExactSizeIterator,
{
// defs and ret constraint are stored consequtively, so we only need to store one index
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
self.constraints.push(Constraint::True);
self.constraints.push(module_constraint);
let let_contraint = LetConstraint {
rigid_vars: self.variable_slice(rigid_vars),
flex_vars: Slice::default(),
def_types: self.def_types_slice(def_types),
defs_and_ret_constraint,
};
let let_index = Index::new(self.let_constraints.len() as _);
self.let_constraints.push(let_contraint);
let pool_slice = self.variable_slice(pool_variables.iter().copied());
Constraint::Let(let_index, pool_slice)
}
#[inline(always)]
@ -408,6 +513,7 @@ impl Constraints {
region,
)
}
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
match constraint {
Constraint::Eq(..) => false,
@ -416,7 +522,7 @@ impl Constraints {
Constraint::Pattern(..) => false,
Constraint::True => false,
Constraint::SaveTheEnvironment => true,
Constraint::Let(index) => {
Constraint::Let(index, _) => {
let let_constraint = &self.let_constraints[index.index()];
let offset = let_constraint.defs_and_ret_constraint.index();
@ -446,35 +552,63 @@ impl Constraints {
filename: &'static str,
line_number: u32,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let type_index = self.push_type(typ);
let string_index = Index::push_new(&mut self.strings, filename);
Constraint::Store(type_index, variable, string_index, line_number)
}
pub fn store_index(
&mut self,
type_index: EitherIndex<Type, Variable>,
variable: Variable,
filename: &'static str,
line_number: u32,
) -> Constraint {
let string_index = Index::push_new(&mut self.strings, filename);
Constraint::Store(type_index, variable, string_index, line_number)
}
}
static_assertions::assert_eq_size!([u8; 3 * 8], Constraint);
roc_error_macros::assert_sizeof_default!(Constraint, 3 * 8);
#[derive(Debug, Clone, PartialEq)]
#[derive(Clone, PartialEq)]
pub enum Constraint {
Eq(Index<Type>, Index<Expected<Type>>, Index<Category>, Region),
Store(Index<Type>, Variable, Index<&'static str>, u32),
Eq(
EitherIndex<Type, Variable>,
Index<Expected<Type>>,
Index<Category>,
Region,
),
Store(
EitherIndex<Type, Variable>,
Variable,
Index<&'static str>,
u32,
),
Lookup(Symbol, Index<Expected<Type>>, Region),
Pattern(
Index<Type>,
EitherIndex<Type, Variable>,
Index<PExpected<Type>>,
Index<PatternCategory>,
Region,
),
True, // Used for things that always unify, e.g. blanks and runtime errors
/// Used for things that always unify, e.g. blanks and runtime errors
True,
SaveTheEnvironment,
Let(Index<LetConstraint>),
/// A Let constraint introduces symbols and their annotation at a certain level of nesting
///
/// The `Slice<Variable>` is used for imports where we manually put the Content into Subs
/// by copying from another module, but have to make sure that any variables we use to store
/// these contents are added to `Pool` at the correct rank
Let(Index<LetConstraint>, Slice<Variable>),
And(Slice<Constraint>),
/// Presence constraints
IsOpenType(Index<Type>), // Theory; always applied to a variable? if yes the use that
IsOpenType(EitherIndex<Type, Variable>), // Theory; always applied to a variable? if yes the use that
IncludesTag(Index<IncludesTag>),
PatternPresence(
Index<Type>,
EitherIndex<Type, Variable>,
Index<PExpected<Type>>,
Index<PatternCategory>,
Region,
@ -503,3 +637,36 @@ pub struct IncludesTag {
pub pattern_category: Index<PatternCategory>,
pub region: Region,
}
/// Custom impl to limit vertical space used by the debug output
impl std::fmt::Debug for Constraint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Eq(arg0, arg1, arg2, arg3) => {
write!(f, "Eq({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
}
Self::Store(arg0, arg1, arg2, arg3) => {
write!(f, "Store({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
}
Self::Lookup(arg0, arg1, arg2) => {
write!(f, "Lookup({:?}, {:?}, {:?})", arg0, arg1, arg2)
}
Self::Pattern(arg0, arg1, arg2, arg3) => {
write!(f, "Pattern({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
}
Self::True => write!(f, "True"),
Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"),
Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(),
Self::And(arg0) => f.debug_tuple("And").field(arg0).finish(),
Self::IsOpenType(arg0) => f.debug_tuple("IsOpenType").field(arg0).finish(),
Self::IncludesTag(arg0) => f.debug_tuple("IncludesTag").field(arg0).finish(),
Self::PatternPresence(arg0, arg1, arg2, arg3) => {
write!(
f,
"PatternPresence({:?}, {:?}, {:?}, {:?})",
arg0, arg1, arg2, arg3
)
}
}
}
}

View file

@ -12,6 +12,7 @@ use crate::procedure::References;
use crate::scope::create_alias;
use crate::scope::Scope;
use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap};
use roc_error_macros::todo_abilities;
use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol;
use roc_parse::ast;
@ -286,21 +287,19 @@ pub fn canonicalize_defs<'a>(
// Record all the annotation's references in output.references.lookups
for symbol in can_ann.references {
output.references.lookups.insert(symbol);
output.references.type_lookups.insert(symbol);
output.references.referenced_type_defs.insert(symbol);
}
let mut can_vars: Vec<Loc<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
let mut is_phantom = false;
let mut var_by_name = can_ann.introduced_variables.var_by_name.clone();
for loc_lowercase in vars.iter() {
if let Some(var) = can_ann
.introduced_variables
.var_by_name(&loc_lowercase.value)
{
if let Some(var) = var_by_name.remove(&loc_lowercase.value) {
// This is a valid lowercase rigid var for the type def.
can_vars.push(Loc {
value: (loc_lowercase.value.clone(), *var),
value: (loc_lowercase.value.clone(), var.value),
region: loc_lowercase.region,
});
} else {
@ -319,6 +318,33 @@ pub fn canonicalize_defs<'a>(
continue;
}
let IntroducedVariables {
wildcards,
inferred,
..
} = can_ann.introduced_variables;
let num_unbound = var_by_name.len() + wildcards.len() + inferred.len();
if num_unbound > 0 {
let one_occurrence = var_by_name
.iter()
.map(|(_, v)| v)
.chain(wildcards.iter())
.chain(inferred.iter())
.next()
.unwrap()
.region;
env.problems.push(Problem::UnboundTypeVariable {
typ: symbol,
num_unbound,
one_occurrence,
kind,
});
// Bail out
continue;
}
let alias = create_alias(
symbol,
name.region,
@ -331,7 +357,7 @@ pub fn canonicalize_defs<'a>(
// Now that we know the alias dependency graph, we can try to insert recursion variables
// where aliases are recursive tag unions, or detect illegal recursions.
let mut aliases = correct_mutual_recursive_type_alias(env, &aliases, var_store);
let mut aliases = correct_mutual_recursive_type_alias(env, aliases, var_store);
for (symbol, alias) in aliases.iter() {
scope.add_alias(
*symbol,
@ -383,7 +409,8 @@ pub fn canonicalize_defs<'a>(
CanDefs {
refs_by_symbol,
can_defs_by_symbol,
aliases,
// The result needs a thread-safe `SendMap`
aliases: aliases.into_iter().collect(),
},
scope,
output,
@ -409,7 +436,7 @@ pub fn sort_can_defs(
// Determine the full set of references by traversing the graph.
let mut visited_symbols = MutSet::default();
let returned_lookups = ImSet::clone(&output.references.lookups);
let returned_lookups = ImSet::clone(&output.references.value_lookups);
// Start with the return expression's referenced locals. They're the only ones that count!
//
@ -482,10 +509,10 @@ pub fn sort_can_defs(
let mut loc_succ = local_successors(references, &env.closures);
// if the current symbol is a closure, peek into its body
if let Some(References { lookups, .. }) = env.closures.get(symbol) {
if let Some(References { value_lookups, .. }) = env.closures.get(symbol) {
let home = env.home;
for lookup in lookups {
for lookup in value_lookups {
if lookup != symbol && lookup.module_id() == home {
// DO NOT register a self-call behind a lambda!
//
@ -532,8 +559,8 @@ pub fn sort_can_defs(
let mut loc_succ = local_successors(references, &env.closures);
// if the current symbol is a closure, peek into its body
if let Some(References { lookups, .. }) = env.closures.get(symbol) {
for lookup in lookups {
if let Some(References { value_lookups, .. }) = env.closures.get(symbol) {
for lookup in value_lookups {
loc_succ.insert(*lookup);
}
}
@ -901,7 +928,7 @@ fn canonicalize_pending_def<'a>(
can_defs_by_symbol: &mut MutMap<Symbol, Def>,
var_store: &mut VarStore,
refs_by_symbol: &mut MutMap<Symbol, (Region, References)>,
aliases: &mut SendMap<Symbol, Alias>,
aliases: &mut ImMap<Symbol, Alias>,
) -> Output {
use PendingDef::*;
@ -919,7 +946,7 @@ fn canonicalize_pending_def<'a>(
// Record all the annotation's references in output.references.lookups
for symbol in type_annotation.references.iter() {
output.references.lookups.insert(*symbol);
output.references.type_lookups.insert(*symbol);
output.references.referenced_type_defs.insert(*symbol);
}
@ -1041,7 +1068,7 @@ fn canonicalize_pending_def<'a>(
// Record all the annotation's references in output.references.lookups
for symbol in type_annotation.references.iter() {
output.references.lookups.insert(*symbol);
output.references.type_lookups.insert(*symbol);
output.references.referenced_type_defs.insert(*symbol);
}
@ -1121,7 +1148,7 @@ fn canonicalize_pending_def<'a>(
// Recursion doesn't count as referencing. (If it did, all recursive functions
// would result in circular def errors!)
refs_by_symbol.entry(symbol).and_modify(|(_, refs)| {
refs.lookups = refs.lookups.without(&symbol);
refs.value_lookups = refs.value_lookups.without(&symbol);
});
// renamed_closure_def = Some(&symbol);
@ -1261,7 +1288,7 @@ fn canonicalize_pending_def<'a>(
// Recursion doesn't count as referencing. (If it did, all recursive functions
// would result in circular def errors!)
refs_by_symbol.entry(symbol).and_modify(|(_, refs)| {
refs.lookups = refs.lookups.without(&symbol);
refs.value_lookups = refs.value_lookups.without(&symbol);
});
loc_can_expr.value = Closure(ClosureData {
@ -1356,7 +1383,8 @@ pub fn can_defs_with_return<'a>(
// Now that we've collected all the references, check to see if any of the new idents
// we defined went unused by the return expression. If any were unused, report it.
for (symbol, region) in symbols_introduced {
if !output.references.has_lookup(symbol) {
if !output.references.has_value_lookup(symbol) && !output.references.has_type_lookup(symbol)
{
env.problem(Problem::UnusedDef(symbol, region));
}
}
@ -1587,6 +1615,8 @@ fn to_pending_def<'a>(
}
}
Ability { .. } => todo_abilities!(),
Expect(_condition) => todo!(),
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {
@ -1625,16 +1655,12 @@ fn pending_typed_body<'a>(
/// Make aliases recursive
fn correct_mutual_recursive_type_alias<'a>(
env: &mut Env<'a>,
original_aliases: &SendMap<Symbol, Alias>,
mut original_aliases: SendMap<Symbol, Alias>,
var_store: &mut VarStore,
) -> SendMap<Symbol, Alias> {
let mut symbols_introduced = ImSet::default();
) -> ImMap<Symbol, Alias> {
let symbols_introduced: Vec<Symbol> = original_aliases.keys().copied().collect();
for (key, _) in original_aliases.iter() {
symbols_introduced.insert(*key);
}
let all_successors_with_self = |symbol: &Symbol| -> ImSet<Symbol> {
let all_successors_with_self = |symbol: &Symbol| -> Vec<Symbol> {
match original_aliases.get(symbol) {
Some(alias) => {
let mut loc_succ = alias.typ.symbols();
@ -1643,7 +1669,7 @@ fn correct_mutual_recursive_type_alias<'a>(
loc_succ
}
None => ImSet::default(),
None => vec![],
}
};
@ -1651,44 +1677,39 @@ fn correct_mutual_recursive_type_alias<'a>(
let defined_symbols: Vec<Symbol> = original_aliases.keys().copied().collect();
let cycles = strongly_connected_components(&defined_symbols, all_successors_with_self);
let mut solved_aliases = SendMap::default();
let mut solved_aliases = ImMap::default();
for cycle in cycles {
debug_assert!(!cycle.is_empty());
let mut pending_aliases: SendMap<_, _> = cycle
let mut pending_aliases: ImMap<_, _> = cycle
.iter()
.map(|&sym| (sym, original_aliases.get(&sym).unwrap().clone()))
.map(|&sym| (sym, original_aliases.remove(&sym).unwrap()))
.collect();
// Make sure we report only one error for the cycle, not an error for every
// alias in the cycle.
let mut can_still_report_error = true;
for &rec in cycle.iter() {
// First, we need to instantiate the alias with any symbols in the currrent module it
// depends on.
// We only need to worry about symbols in this SCC or any prior one, since the SCCs
// were sorted topologically, and we've already instantiated aliases coming from other
// modules.
let mut to_instantiate: ImMap<_, _> = solved_aliases.clone().into_iter().collect();
let mut others_in_scc = Vec::with_capacity(cycle.len() - 1);
for &other in cycle.iter() {
if rec != other {
others_in_scc.push(other);
if let Some(alias) = original_aliases.get(&other) {
to_instantiate.insert(other, alias.clone());
}
}
}
// We need to instantiate the alias with any symbols in the currrent module it
// depends on.
// We only need to worry about symbols in this SCC or any prior one, since the SCCs
// were sorted topologically, and we've already instantiated aliases coming from other
// modules.
// NB: ImMap::clone is O(1): https://docs.rs/im/latest/src/im/hash/map.rs.html#1527-1544
let mut to_instantiate = solved_aliases.clone().union(pending_aliases.clone());
for &rec in cycle.iter() {
let alias = pending_aliases.get_mut(&rec).unwrap();
// Don't try to instantiate the alias itself in its definition.
let original_alias_def = to_instantiate.remove(&rec).unwrap();
alias.typ.instantiate_aliases(
alias.region,
&to_instantiate,
var_store,
&mut ImSet::default(),
);
to_instantiate.insert(rec, original_alias_def);
// Now mark the alias recursive, if it needs to be.
let is_self_recursive = alias.typ.contains_symbol(rec);
@ -1752,7 +1773,7 @@ fn make_tag_union_of_alias_recursive<'a>(
.map(|l| (l.value.0.clone(), Type::Variable(l.value.1)))
.collect::<Vec<_>>();
make_tag_union_recursive_help(
let made_recursive = make_tag_union_recursive_help(
env,
Loc::at(alias.header_region(), (alias_name, &alias_args)),
alias.region,
@ -1760,7 +1781,24 @@ fn make_tag_union_of_alias_recursive<'a>(
&mut alias.typ,
var_store,
can_report_error,
)
);
match made_recursive {
MakeTagUnionRecursive::Cyclic => Ok(()),
MakeTagUnionRecursive::MadeRecursive { recursion_variable } => {
alias.recursion_variables.clear();
alias.recursion_variables.insert(recursion_variable);
Ok(())
}
MakeTagUnionRecursive::InvalidRecursion => Err(()),
}
}
enum MakeTagUnionRecursive {
Cyclic,
MadeRecursive { recursion_variable: Variable },
InvalidRecursion,
}
/// Attempt to make a tag union recursive at the position of `recursive_alias`; for example,
@ -1792,7 +1830,9 @@ fn make_tag_union_recursive_help<'a>(
typ: &mut Type,
var_store: &mut VarStore,
can_report_error: &mut bool,
) -> Result<(), ()> {
) -> MakeTagUnionRecursive {
use MakeTagUnionRecursive::*;
let Loc {
value: (symbol, args),
region: alias_region,
@ -1800,15 +1840,17 @@ fn make_tag_union_recursive_help<'a>(
let vars = args.iter().map(|(_, t)| t.clone()).collect::<Vec<_>>();
match typ {
Type::TagUnion(tags, ext) => {
let rec_var = var_store.fresh();
let mut pending_typ = Type::RecursiveTagUnion(rec_var, tags.to_vec(), ext.clone());
let recursion_variable = var_store.fresh();
let mut pending_typ =
Type::RecursiveTagUnion(recursion_variable, tags.to_vec(), ext.clone());
let substitution_result =
pending_typ.substitute_alias(symbol, &vars, &Type::Variable(rec_var));
pending_typ.substitute_alias(symbol, &vars, &Type::Variable(recursion_variable));
match substitution_result {
Ok(()) => {
// We can substitute the alias presence for the variable exactly.
*typ = pending_typ;
Ok(())
MadeRecursive { recursion_variable }
}
Err(differing_recursion_region) => {
env.problems.push(Problem::NestedDatatype {
@ -1816,11 +1858,14 @@ fn make_tag_union_recursive_help<'a>(
def_region: alias_region,
differing_recursion_region,
});
Err(())
InvalidRecursion
}
}
}
Type::RecursiveTagUnion(_, _, _) => Ok(()),
Type::RecursiveTagUnion(recursion_variable, _, _) => MadeRecursive {
recursion_variable: *recursion_variable,
},
Type::Alias {
actual,
type_arguments,
@ -1838,7 +1883,7 @@ fn make_tag_union_recursive_help<'a>(
mark_cyclic_alias(env, typ, symbol, region, others, *can_report_error);
*can_report_error = false;
Ok(())
Cyclic
}
}
}

View file

@ -10,7 +10,7 @@ use roc_module::ident::TagName;
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{AliasKind, Type};
use roc_types::types::{AliasKind, Type, TypeExtension};
#[derive(Debug, Default, Clone, Copy)]
pub(crate) struct HostedGeneratedFunctions {
@ -210,7 +210,7 @@ fn build_effect_always(
let signature = {
// Effect.always : a -> Effect a
let var_a = var_store.fresh();
introduced_variables.insert_named("a".into(), var_a);
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
let effect_a = build_effect_alias(
effect_symbol,
@ -223,7 +223,7 @@ fn build_effect_always(
);
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
Type::Function(
vec![Type::Variable(var_a)],
@ -402,8 +402,8 @@ fn build_effect_map(
let var_a = var_store.fresh();
let var_b = var_store.fresh();
introduced_variables.insert_named("a".into(), var_a);
introduced_variables.insert_named("b".into(), var_b);
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_a = build_effect_alias(
effect_symbol,
@ -426,7 +426,7 @@ fn build_effect_map(
);
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
let a_to_b = {
Type::Function(
vec![Type::Variable(var_a)],
@ -436,7 +436,7 @@ fn build_effect_map(
};
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
Type::Function(
vec![effect_a, a_to_b],
Box::new(Type::Variable(closure_var)),
@ -571,8 +571,8 @@ fn build_effect_after(
let var_a = var_store.fresh();
let var_b = var_store.fresh();
introduced_variables.insert_named("a".into(), var_a);
introduced_variables.insert_named("b".into(), var_b);
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_a = build_effect_alias(
effect_symbol,
@ -595,7 +595,7 @@ fn build_effect_after(
);
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
let a_to_effect_b = Type::Function(
vec![Type::Variable(var_a)],
Box::new(Type::Variable(closure_var)),
@ -603,7 +603,7 @@ fn build_effect_after(
);
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
Type::Function(
vec![effect_a, a_to_effect_b],
Box::new(Type::Variable(closure_var)),
@ -831,8 +831,8 @@ fn build_effect_forever(
let var_a = var_store.fresh();
let var_b = var_store.fresh();
introduced_variables.insert_named("a".into(), var_a);
introduced_variables.insert_named("b".into(), var_b);
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_a = build_effect_alias(
effect_symbol,
@ -855,7 +855,7 @@ fn build_effect_forever(
);
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
Type::Function(
vec![effect_a],
@ -1089,8 +1089,8 @@ fn build_effect_loop(
let var_a = var_store.fresh();
let var_b = var_store.fresh();
introduced_variables.insert_named("a".into(), var_a);
introduced_variables.insert_named("b".into(), var_b);
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_b = build_effect_alias(
effect_symbol,
@ -1111,13 +1111,13 @@ fn build_effect_loop(
(step_tag_name, vec![Type::Variable(var_a)]),
(done_tag_name, vec![Type::Variable(var_b)]),
],
Box::new(Type::EmptyTagUnion),
TypeExtension::Closed,
)
};
let effect_state_type = {
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
let actual = {
Type::TagUnion(
@ -1129,7 +1129,7 @@ fn build_effect_loop(
Box::new(state_type.clone()),
)],
)],
Box::new(Type::EmptyTagUnion),
TypeExtension::Closed,
)
};
@ -1145,7 +1145,7 @@ fn build_effect_loop(
};
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
let step_type = Type::Function(
vec![Type::Variable(var_a)],
@ -1154,7 +1154,7 @@ fn build_effect_loop(
);
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
Type::Function(
vec![Type::Variable(var_a), step_type],
@ -1559,7 +1559,7 @@ fn build_effect_alias(
introduced_variables: &mut IntroducedVariables,
) -> Type {
let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(closure_var);
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
let actual = {
Type::TagUnion(
@ -1571,7 +1571,7 @@ fn build_effect_alias(
Box::new(a_type),
)],
)],
Box::new(Type::EmptyTagUnion),
TypeExtension::Closed,
)
};
@ -1600,7 +1600,7 @@ pub fn build_effect_actual(
Box::new(a_type),
)],
)],
Box::new(Type::EmptyTagUnion),
TypeExtension::Closed,
)
}

View file

@ -27,8 +27,11 @@ pub struct Env<'a> {
/// current closure name (if any)
pub closure_name_symbol: Option<Symbol>,
/// Symbols which were referenced by qualified lookups.
pub qualified_lookups: MutSet<Symbol>,
/// Symbols of values/functions which were referenced by qualified lookups.
pub qualified_value_lookups: MutSet<Symbol>,
/// Symbols of types which were referenced by qualified lookups.
pub qualified_type_lookups: MutSet<Symbol>,
pub top_level_symbols: MutSet<Symbol>,
@ -51,7 +54,8 @@ impl<'a> Env<'a> {
exposed_ident_ids,
problems: Vec::new(),
closures: MutMap::default(),
qualified_lookups: MutSet::default(),
qualified_value_lookups: MutSet::default(),
qualified_type_lookups: MutSet::default(),
tailcallable_symbol: None,
closure_name_symbol: None,
top_level_symbols: MutSet::default(),
@ -71,6 +75,8 @@ impl<'a> Env<'a> {
ident
);
let is_type_name = ident.starts_with(|c: char| c.is_uppercase());
let module_name = ModuleName::from(module_name_str);
let ident = Ident::from(ident);
@ -83,7 +89,11 @@ impl<'a> Env<'a> {
Some(ident_id) => {
let symbol = Symbol::new(module_id, *ident_id);
self.qualified_lookups.insert(symbol);
if is_type_name {
self.qualified_type_lookups.insert(symbol);
} else {
self.qualified_value_lookups.insert(symbol);
}
Ok(symbol)
}
@ -107,7 +117,11 @@ impl<'a> Env<'a> {
Some(ident_id) => {
let symbol = Symbol::new(module_id, *ident_id);
self.qualified_lookups.insert(symbol);
if is_type_name {
self.qualified_type_lookups.insert(symbol);
} else {
self.qualified_value_lookups.insert(symbol);
}
Ok(symbol)
}

View file

@ -493,7 +493,7 @@ pub fn canonicalize_expr<'a>(
Ok((name, opaque_def)) => {
let argument = Box::new(args.pop().unwrap());
output.references.referenced_type_defs.insert(name);
output.references.lookups.insert(name);
output.references.type_lookups.insert(name);
let (type_arguments, lambda_set_variables, specialized_def_type) =
freshen_opaque_def(var_store, opaque_def);
@ -587,7 +587,7 @@ pub fn canonicalize_expr<'a>(
}
}
ast::Expr::Var { module_name, ident } => {
canonicalize_lookup(env, scope, module_name, ident, region)
canonicalize_var_lookup(env, scope, module_name, ident, region)
}
ast::Expr::Underscore(name) => {
// we parse underscores, but they are not valid expression syntax
@ -661,8 +661,12 @@ pub fn canonicalize_expr<'a>(
&loc_body_expr.value,
);
let mut captured_symbols: MutSet<Symbol> =
new_output.references.lookups.iter().copied().collect();
let mut captured_symbols: MutSet<Symbol> = new_output
.references
.value_lookups
.iter()
.copied()
.collect();
// filter out the closure's name itself
captured_symbols.remove(&symbol);
@ -684,7 +688,10 @@ pub fn canonicalize_expr<'a>(
output.union(new_output);
// filter out aliases
captured_symbols.retain(|s| !output.references.referenced_type_defs.contains(s));
debug_assert!(captured_symbols
.iter()
.all(|s| !output.references.referenced_type_defs.contains(s)));
// captured_symbols.retain(|s| !output.references.referenced_type_defs.contains(s));
// filter out functions that don't close over anything
captured_symbols.retain(|s| !output.non_closures.contains(s));
@ -693,7 +700,7 @@ pub fn canonicalize_expr<'a>(
// went unreferenced. If any did, report them as unused arguments.
for (sub_symbol, region) in scope.symbols() {
if !original_scope.contains_symbol(*sub_symbol) {
if !output.references.has_lookup(*sub_symbol) {
if !output.references.has_value_lookup(*sub_symbol) {
// The body never referenced this argument we declared. It's an unused argument!
env.problem(Problem::UnusedArgument(symbol, *sub_symbol, *region));
}
@ -701,7 +708,7 @@ pub fn canonicalize_expr<'a>(
// We shouldn't ultimately count arguments as referenced locals. Otherwise,
// we end up with weird conclusions like the expression (\x -> x + 1)
// references the (nonexistent) local variable x!
output.references.lookups.remove(sub_symbol);
output.references.value_lookups.remove(sub_symbol);
}
}
@ -1082,8 +1089,10 @@ fn canonicalize_when_branch<'a>(
for (symbol, region) in scope.symbols() {
let symbol = *symbol;
if !output.references.has_lookup(symbol)
&& !branch_output.references.has_lookup(symbol)
if !output.references.has_value_lookup(symbol)
&& !output.references.has_type_lookup(symbol)
&& !branch_output.references.has_value_lookup(symbol)
&& !branch_output.references.has_type_lookup(symbol)
&& !original_scope.contains_symbol(symbol)
{
env.problem(Problem::UnusedDef(symbol, *region));
@ -1107,7 +1116,7 @@ pub fn local_successors<'a>(
references: &'a References,
closures: &'a MutMap<Symbol, References>,
) -> ImSet<Symbol> {
let mut answer = references.lookups.clone();
let mut answer = references.value_lookups.clone();
for call_symbol in references.calls.iter() {
answer = answer.union(call_successors(*call_symbol, closures));
@ -1127,7 +1136,7 @@ fn call_successors(call_symbol: Symbol, closures: &MutMap<Symbol, References>) -
}
if let Some(references) = closures.get(&symbol) {
answer.extend(references.lookups.iter().copied());
answer.extend(references.value_lookups.iter().copied());
queue.extend(references.calls.iter().copied());
seen.insert(symbol);
@ -1152,7 +1161,7 @@ where
Some((_, refs)) => {
visited.insert(defined_symbol);
for local in refs.lookups.iter() {
for local in refs.value_lookups.iter() {
if !visited.contains(local) {
let other_refs: References =
references_from_local(*local, visited, refs_by_def, closures);
@ -1160,7 +1169,7 @@ where
answer = answer.union(other_refs);
}
answer.lookups.insert(*local);
answer.value_lookups.insert(*local);
}
for call in refs.calls.iter() {
@ -1194,7 +1203,7 @@ where
visited.insert(call_symbol);
for closed_over_local in references.lookups.iter() {
for closed_over_local in references.value_lookups.iter() {
if !visited.contains(closed_over_local) {
let other_refs =
references_from_local(*closed_over_local, visited, refs_by_def, closures);
@ -1202,7 +1211,7 @@ where
answer = answer.union(other_refs);
}
answer.lookups.insert(*closed_over_local);
answer.value_lookups.insert(*closed_over_local);
}
for call in references.calls.iter() {
@ -1335,7 +1344,7 @@ fn canonicalize_field<'a>(
}
}
fn canonicalize_lookup(
fn canonicalize_var_lookup(
env: &mut Env<'_>,
scope: &mut Scope,
module_name: &str,
@ -1350,7 +1359,7 @@ fn canonicalize_lookup(
// Look it up in scope!
match scope.lookup(&(*ident).into(), region) {
Ok(symbol) => {
output.references.lookups.insert(symbol);
output.references.value_lookups.insert(symbol);
Var(symbol)
}
@ -1365,7 +1374,7 @@ fn canonicalize_lookup(
// Look it up in the env!
match env.qualified_lookup(module_name, ident, region) {
Ok(symbol) => {
output.references.lookups.insert(symbol);
output.references.value_lookups.insert(symbol);
Var(symbol)
}

View file

@ -23,21 +23,29 @@ pub struct Module {
pub module_id: ModuleId,
pub exposed_imports: MutMap<Symbol, Variable>,
pub exposed_symbols: MutSet<Symbol>,
pub references: MutSet<Symbol>,
pub referenced_values: MutSet<Symbol>,
pub referenced_types: MutSet<Symbol>,
pub aliases: MutMap<Symbol, Alias>,
pub rigid_variables: MutMap<Variable, Lowercase>,
pub rigid_variables: RigidVariables,
}
#[derive(Debug, Default)]
pub struct RigidVariables {
pub named: MutMap<Variable, Lowercase>,
pub wildcards: MutSet<Variable>,
}
#[derive(Debug)]
pub struct ModuleOutput {
pub aliases: MutMap<Symbol, Alias>,
pub rigid_variables: MutMap<Variable, Lowercase>,
pub rigid_variables: RigidVariables,
pub declarations: Vec<Declaration>,
pub exposed_imports: MutMap<Symbol, Variable>,
pub lookups: Vec<(Symbol, Variable, Region)>,
pub problems: Vec<Problem>,
pub ident_ids: IdentIds,
pub references: MutSet<Symbol>,
pub referenced_values: MutSet<Symbol>,
pub referenced_types: MutSet<Symbol>,
pub scope: Scope,
}
@ -206,7 +214,7 @@ pub fn canonicalize_module_defs<'a>(
}
let mut lookups = Vec::with_capacity(num_deps);
let mut rigid_variables = MutMap::default();
let mut rigid_variables = RigidVariables::default();
// Exposed values are treated like defs that appear before any others, e.g.
//
@ -291,38 +299,38 @@ pub fn canonicalize_module_defs<'a>(
// See if any of the new idents we defined went unused.
// If any were unused and also not exposed, report it.
for (symbol, region) in symbols_introduced {
if !output.references.has_lookup(symbol) && !exposed_symbols.contains(&symbol) {
if !output.references.has_value_lookup(symbol)
&& !output.references.has_type_lookup(symbol)
&& !exposed_symbols.contains(&symbol)
{
env.problem(Problem::UnusedDef(symbol, region));
}
}
for (var, lowercase) in output.introduced_variables.name_by_var {
rigid_variables.insert(var, lowercase.clone());
rigid_variables.named.insert(var, lowercase.clone());
}
for var in output.introduced_variables.wildcards {
rigid_variables.insert(var, "*".into());
rigid_variables.wildcards.insert(var.value);
}
let mut references = MutSet::default();
let mut referenced_values = MutSet::default();
let mut referenced_types = MutSet::default();
// Gather up all the symbols that were referenced across all the defs' lookups.
for symbol in output.references.lookups.iter() {
references.insert(*symbol);
}
referenced_values.extend(output.references.value_lookups);
referenced_types.extend(output.references.type_lookups);
// Gather up all the symbols that were referenced across all the defs' calls.
for symbol in output.references.calls.iter() {
references.insert(*symbol);
}
referenced_values.extend(output.references.calls);
// Gather up all the symbols that were referenced from other modules.
for symbol in env.qualified_lookups.iter() {
references.insert(*symbol);
}
referenced_values.extend(env.qualified_value_lookups.iter().copied());
referenced_types.extend(env.qualified_type_lookups.iter().copied());
// add any builtins used by other builtins
let transitive_builtins: Vec<Symbol> = references
let transitive_builtins: Vec<Symbol> = referenced_values
.iter()
.filter(|s| s.is_builtin())
.map(|s| crate::builtins::builtin_dependencies(*s))
@ -330,7 +338,7 @@ pub fn canonicalize_module_defs<'a>(
.copied()
.collect();
references.extend(transitive_builtins);
referenced_values.extend(transitive_builtins);
// NOTE previously we inserted builtin defs into the list of defs here
// this is now done later, in file.rs.
@ -340,7 +348,12 @@ pub fn canonicalize_module_defs<'a>(
// symbols from this set
let mut exposed_but_not_defined = exposed_symbols.clone();
match sort_can_defs(&mut env, defs, Output::default()) {
let new_output = Output {
aliases: output.aliases,
..Default::default()
};
match sort_can_defs(&mut env, defs, new_output) {
(Ok(mut declarations), output) => {
use crate::def::Declaration::*;
@ -504,19 +517,15 @@ pub fn canonicalize_module_defs<'a>(
}
// Incorporate any remaining output.lookups entries into references.
for symbol in output.references.lookups {
references.insert(symbol);
}
referenced_values.extend(output.references.value_lookups);
referenced_types.extend(output.references.type_lookups);
// Incorporate any remaining output.calls entries into references.
for symbol in output.references.calls {
references.insert(symbol);
}
referenced_values.extend(output.references.calls);
// Gather up all the symbols that were referenced from other modules.
for symbol in env.qualified_lookups.iter() {
references.insert(*symbol);
}
referenced_values.extend(env.qualified_value_lookups.iter().copied());
referenced_types.extend(env.qualified_type_lookups.iter().copied());
for declaration in declarations.iter_mut() {
match declaration {
@ -530,7 +539,7 @@ pub fn canonicalize_module_defs<'a>(
// TODO this loops over all symbols in the module, we can speed it up by having an
// iterator over all builtin symbols
for symbol in references.iter() {
for symbol in referenced_values.iter() {
if symbol.is_builtin() {
// this can fail when the symbol is for builtin types, or has no implementation yet
if let Some(def) = crate::builtins::builtin_defs_map(*symbol, var_store) {
@ -544,7 +553,8 @@ pub fn canonicalize_module_defs<'a>(
aliases,
rigid_variables,
declarations,
references,
referenced_values,
referenced_types,
exposed_imports: can_exposed_imports,
problems: env.problems,
lookups,

View file

@ -96,6 +96,7 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
SpaceBefore(def, _) | SpaceAfter(def, _) => desugar_def(arena, def),
alias @ Alias { .. } => *alias,
opaque @ Opaque { .. } => *opaque,
ability @ Ability { .. } => *ability,
ann @ Annotation(_, _) => *ann,
AnnotatedBody {
ann_pattern,

View file

@ -254,7 +254,7 @@ pub fn canonicalize_pattern<'a>(
freshen_opaque_def(var_store, opaque_def);
output.references.referenced_type_defs.insert(opaque);
output.references.lookups.insert(opaque);
output.references.type_lookups.insert(opaque);
Pattern::UnwrappedOpaque {
whole_var: var_store.fresh(),

View file

@ -45,7 +45,8 @@ impl Procedure {
#[derive(Clone, Debug, Default, PartialEq)]
pub struct References {
pub bound_symbols: ImSet<Symbol>,
pub lookups: ImSet<Symbol>,
pub type_lookups: ImSet<Symbol>,
pub value_lookups: ImSet<Symbol>,
/// Aliases or opaque types referenced
pub referenced_type_defs: ImSet<Symbol>,
pub calls: ImSet<Symbol>,
@ -57,7 +58,8 @@ impl References {
}
pub fn union(mut self, other: References) -> Self {
self.lookups = self.lookups.union(other.lookups);
self.value_lookups = self.value_lookups.union(other.value_lookups);
self.type_lookups = self.type_lookups.union(other.type_lookups);
self.calls = self.calls.union(other.calls);
self.bound_symbols = self.bound_symbols.union(other.bound_symbols);
self.referenced_type_defs = self.referenced_type_defs.union(other.referenced_type_defs);
@ -66,13 +68,18 @@ impl References {
}
pub fn union_mut(&mut self, other: References) {
self.lookups.extend(other.lookups);
self.value_lookups.extend(other.value_lookups);
self.type_lookups.extend(other.type_lookups);
self.calls.extend(other.calls);
self.bound_symbols.extend(other.bound_symbols);
self.referenced_type_defs.extend(other.referenced_type_defs);
}
pub fn has_lookup(&self, symbol: Symbol) -> bool {
self.lookups.contains(&symbol)
pub fn has_value_lookup(&self, symbol: Symbol) -> bool {
self.value_lookups.contains(&symbol)
}
pub fn has_type_lookup(&self, symbol: Symbol) -> bool {
self.type_lookups.contains(&symbol)
}
}