mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge pull request #2910 from rtfeldman/i/2880
Ability codegen for Encode/Decode
This commit is contained in:
commit
3e7702df01
28 changed files with 1235 additions and 378 deletions
|
@ -6,6 +6,8 @@ use roc_types::{subs::Variable, types::Type};
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct MemberVariables {
|
pub struct MemberVariables {
|
||||||
pub able_vars: Vec<Variable>,
|
pub able_vars: Vec<Variable>,
|
||||||
|
/// This includes - named rigid vars, lambda sets, wildcards. See
|
||||||
|
/// [`IntroducedVariables::collect_rigid`](crate::annotation::IntroducedVariables::collect_rigid).
|
||||||
pub rigid_vars: Vec<Variable>,
|
pub rigid_vars: Vec<Variable>,
|
||||||
pub flex_vars: Vec<Variable>,
|
pub flex_vars: Vec<Variable>,
|
||||||
}
|
}
|
||||||
|
@ -13,7 +15,7 @@ pub struct MemberVariables {
|
||||||
/// Stores information about an ability member definition, including the parent ability, the
|
/// Stores information about an ability member definition, including the parent ability, the
|
||||||
/// defining type, and what type variables need to be instantiated with instances of the ability.
|
/// defining type, and what type variables need to be instantiated with instances of the ability.
|
||||||
// TODO: SoA and put me in an arena
|
// TODO: SoA and put me in an arena
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AbilityMemberData {
|
pub struct AbilityMemberData {
|
||||||
pub parent_ability: Symbol,
|
pub parent_ability: Symbol,
|
||||||
pub signature_var: Variable,
|
pub signature_var: Variable,
|
||||||
|
@ -29,11 +31,22 @@ pub struct MemberSpecialization {
|
||||||
pub region: Region,
|
pub region: Region,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SpecializationId(u64);
|
||||||
|
|
||||||
|
#[allow(clippy::derivable_impls)] // let's be explicit about this
|
||||||
|
impl Default for SpecializationId {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Stores information about what abilities exist in a scope, what it means to implement an
|
/// Stores information about what abilities exist in a scope, what it means to implement an
|
||||||
/// ability, and what types implement them.
|
/// ability, and what types implement them.
|
||||||
// TODO(abilities): this should probably go on the Scope, I don't put it there for now because we
|
// TODO(abilities): this should probably go on the Scope, I don't put it there for now because we
|
||||||
// are only dealing with inter-module abilities for now.
|
// are only dealing with intra-module abilities for now.
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
// TODO(abilities): many of these should be `VecMap`s. Do some benchmarking.
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct AbilitiesStore {
|
pub struct AbilitiesStore {
|
||||||
/// Maps an ability to the members defining it.
|
/// Maps an ability to the members defining it.
|
||||||
members_of_ability: MutMap<Symbol, Vec<Symbol>>,
|
members_of_ability: MutMap<Symbol, Vec<Symbol>>,
|
||||||
|
@ -54,6 +67,12 @@ pub struct AbilitiesStore {
|
||||||
/// Maps a tuple (member, type) specifying that `type` declares an implementation of an ability
|
/// Maps a tuple (member, type) specifying that `type` declares an implementation of an ability
|
||||||
/// member `member`, to the exact symbol that implements the ability.
|
/// member `member`, to the exact symbol that implements the ability.
|
||||||
declared_specializations: MutMap<(Symbol, Symbol), MemberSpecialization>,
|
declared_specializations: MutMap<(Symbol, Symbol), MemberSpecialization>,
|
||||||
|
|
||||||
|
next_specialization_id: u64,
|
||||||
|
|
||||||
|
/// Resolved specializations for a symbol. These might be ephemeral (known due to type solving),
|
||||||
|
/// or resolved on-the-fly during mono.
|
||||||
|
resolved_specializations: MutMap<SpecializationId, Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbilitiesStore {
|
impl AbilitiesStore {
|
||||||
|
@ -168,4 +187,37 @@ impl AbilitiesStore {
|
||||||
pub fn members_of_ability(&self, ability: Symbol) -> Option<&[Symbol]> {
|
pub fn members_of_ability(&self, ability: Symbol) -> Option<&[Symbol]> {
|
||||||
self.members_of_ability.get(&ability).map(|v| v.as_ref())
|
self.members_of_ability.get(&ability).map(|v| v.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fresh_specialization_id(&mut self) -> SpecializationId {
|
||||||
|
debug_assert!(self.next_specialization_id != std::u64::MAX);
|
||||||
|
|
||||||
|
let id = SpecializationId(self.next_specialization_id);
|
||||||
|
self.next_specialization_id += 1;
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_resolved(&mut self, id: SpecializationId, specialization: Symbol) {
|
||||||
|
debug_assert!(self.is_specialization_name(specialization));
|
||||||
|
|
||||||
|
let old_specialization = self.resolved_specializations.insert(id, specialization);
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
old_specialization.is_none(),
|
||||||
|
"Existing resolution: {:?}",
|
||||||
|
old_specialization
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_resolved(&mut self, id: SpecializationId) {
|
||||||
|
let old_specialization = self.resolved_specializations.remove(&id);
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
old_specialization.is_some(),
|
||||||
|
"Trying to remove a resolved specialization that was never there!",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_resolved(&self, id: SpecializationId) -> Option<Symbol> {
|
||||||
|
self.resolved_specializations.get(&id).copied()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ use roc_problem::can::ShadowKind;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
name_type_var, Alias, AliasCommon, AliasKind, LambdaSet, Problem, RecordField, Type,
|
name_type_var, Alias, AliasCommon, AliasKind, AliasVar, LambdaSet, OptAbleType, OptAbleVar,
|
||||||
TypeExtension,
|
Problem, RecordField, Type, TypeExtension,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -71,6 +71,48 @@ impl<'a> NamedOrAbleVariable<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum OwnedNamedOrAble {
|
||||||
|
Named(NamedVariable),
|
||||||
|
Able(AbleVariable),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedNamedOrAble {
|
||||||
|
pub fn first_seen(&self) -> Region {
|
||||||
|
match self {
|
||||||
|
OwnedNamedOrAble::Named(nv) => nv.first_seen,
|
||||||
|
OwnedNamedOrAble::Able(av) => av.first_seen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ref_name(&self) -> &Lowercase {
|
||||||
|
match self {
|
||||||
|
OwnedNamedOrAble::Named(nv) => &nv.name,
|
||||||
|
OwnedNamedOrAble::Able(av) => &av.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(self) -> Lowercase {
|
||||||
|
match self {
|
||||||
|
OwnedNamedOrAble::Named(nv) => nv.name,
|
||||||
|
OwnedNamedOrAble::Able(av) => av.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable(&self) -> Variable {
|
||||||
|
match self {
|
||||||
|
OwnedNamedOrAble::Named(nv) => nv.variable,
|
||||||
|
OwnedNamedOrAble::Able(av) => av.variable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opt_ability(&self) -> Option<Symbol> {
|
||||||
|
match self {
|
||||||
|
OwnedNamedOrAble::Named(_) => None,
|
||||||
|
OwnedNamedOrAble::Able(av) => Some(av.ability),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A named type variable, not bound to an ability.
|
/// A named type variable, not bound to an ability.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct NamedVariable {
|
pub struct NamedVariable {
|
||||||
|
@ -603,7 +645,7 @@ fn can_annotation_help(
|
||||||
references,
|
references,
|
||||||
);
|
);
|
||||||
let mut vars = Vec::with_capacity(loc_vars.len());
|
let mut vars = Vec::with_capacity(loc_vars.len());
|
||||||
let mut lowercase_vars = Vec::with_capacity(loc_vars.len());
|
let mut lowercase_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(loc_vars.len());
|
||||||
|
|
||||||
references.insert(symbol);
|
references.insert(symbol);
|
||||||
|
|
||||||
|
@ -616,9 +658,17 @@ fn can_annotation_help(
|
||||||
};
|
};
|
||||||
let var_name = Lowercase::from(var);
|
let var_name = Lowercase::from(var);
|
||||||
|
|
||||||
|
// TODO(abilities): check that there are no abilities bound here.
|
||||||
if let Some(var) = introduced_variables.var_by_name(&var_name) {
|
if let Some(var) = introduced_variables.var_by_name(&var_name) {
|
||||||
vars.push(Type::Variable(var));
|
vars.push(Type::Variable(var));
|
||||||
lowercase_vars.push(Loc::at(loc_var.region, (var_name, var)));
|
lowercase_vars.push(Loc::at(
|
||||||
|
loc_var.region,
|
||||||
|
AliasVar {
|
||||||
|
name: var_name,
|
||||||
|
var,
|
||||||
|
opt_bound_ability: None,
|
||||||
|
},
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
|
@ -626,7 +676,14 @@ fn can_annotation_help(
|
||||||
.insert_named(var_name.clone(), Loc::at(loc_var.region, var));
|
.insert_named(var_name.clone(), Loc::at(loc_var.region, var));
|
||||||
vars.push(Type::Variable(var));
|
vars.push(Type::Variable(var));
|
||||||
|
|
||||||
lowercase_vars.push(Loc::at(loc_var.region, (var_name, var)));
|
lowercase_vars.push(Loc::at(
|
||||||
|
loc_var.region,
|
||||||
|
AliasVar {
|
||||||
|
name: var_name,
|
||||||
|
var,
|
||||||
|
opt_bound_ability: None,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,7 +732,7 @@ fn can_annotation_help(
|
||||||
hidden_variables.extend(alias_actual.variables());
|
hidden_variables.extend(alias_actual.variables());
|
||||||
|
|
||||||
for loc_var in lowercase_vars.iter() {
|
for loc_var in lowercase_vars.iter() {
|
||||||
hidden_variables.remove(&loc_var.value.1);
|
hidden_variables.remove(&loc_var.value.var);
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.add_alias(
|
scope.add_alias(
|
||||||
|
@ -702,7 +759,13 @@ fn can_annotation_help(
|
||||||
} else {
|
} else {
|
||||||
Type::Alias {
|
Type::Alias {
|
||||||
symbol,
|
symbol,
|
||||||
type_arguments: vars,
|
type_arguments: vars
|
||||||
|
.into_iter()
|
||||||
|
.map(|typ| OptAbleType {
|
||||||
|
typ,
|
||||||
|
opt_ability: None,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
lambda_set_variables: alias.lambda_set_variables.clone(),
|
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||||
actual: Box::new(alias.typ.clone()),
|
actual: Box::new(alias.typ.clone()),
|
||||||
kind: alias.kind,
|
kind: alias.kind,
|
||||||
|
@ -995,7 +1058,7 @@ fn shallow_dealias_with_scope<'a>(scope: &'a mut Scope, typ: &'a Type) -> &'a Ty
|
||||||
pub fn instantiate_and_freshen_alias_type(
|
pub fn instantiate_and_freshen_alias_type(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
type_variables: &[Loc<(Lowercase, Variable)>],
|
type_variables: &[Loc<AliasVar>],
|
||||||
type_arguments: Vec<Type>,
|
type_arguments: Vec<Type>,
|
||||||
lambda_set_variables: &[LambdaSet],
|
lambda_set_variables: &[LambdaSet],
|
||||||
mut actual_type: Type,
|
mut actual_type: Type,
|
||||||
|
@ -1004,8 +1067,8 @@ pub fn instantiate_and_freshen_alias_type(
|
||||||
let mut type_var_to_arg = Vec::new();
|
let mut type_var_to_arg = Vec::new();
|
||||||
|
|
||||||
for (loc_var, arg_ann) in type_variables.iter().zip(type_arguments.into_iter()) {
|
for (loc_var, arg_ann) in type_variables.iter().zip(type_arguments.into_iter()) {
|
||||||
let name = loc_var.value.0.clone();
|
let name = loc_var.value.name.clone();
|
||||||
let var = loc_var.value.1;
|
let var = loc_var.value.var;
|
||||||
|
|
||||||
substitutions.insert(var, arg_ann.clone());
|
substitutions.insert(var, arg_ann.clone());
|
||||||
type_var_to_arg.push((name.clone(), arg_ann));
|
type_var_to_arg.push((name.clone(), arg_ann));
|
||||||
|
@ -1040,19 +1103,21 @@ pub fn instantiate_and_freshen_alias_type(
|
||||||
pub fn freshen_opaque_def(
|
pub fn freshen_opaque_def(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
opaque: &Alias,
|
opaque: &Alias,
|
||||||
) -> (Vec<Variable>, Vec<LambdaSet>, Type) {
|
) -> (Vec<OptAbleVar>, Vec<LambdaSet>, Type) {
|
||||||
debug_assert!(opaque.kind == AliasKind::Opaque);
|
debug_assert!(opaque.kind == AliasKind::Opaque);
|
||||||
|
|
||||||
let fresh_variables: Vec<Variable> = opaque
|
let fresh_variables: Vec<OptAbleVar> = opaque
|
||||||
.type_variables
|
.type_variables
|
||||||
.iter()
|
.iter()
|
||||||
.map(|_| var_store.fresh())
|
.map(|alias_var| OptAbleVar {
|
||||||
|
var: var_store.fresh(),
|
||||||
|
opt_ability: alias_var.value.opt_bound_ability,
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let fresh_type_arguments = fresh_variables
|
let fresh_type_arguments = fresh_variables
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.map(|av| Type::Variable(av.var))
|
||||||
.map(Type::Variable)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// NB: We don't introduce the fresh variables here, we introduce them during constraint gen.
|
// NB: We don't introduce the fresh variables here, we introduce them during constraint gen.
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::abilities::MemberVariables;
|
||||||
use crate::annotation::canonicalize_annotation;
|
use crate::annotation::canonicalize_annotation;
|
||||||
use crate::annotation::find_type_def_symbols;
|
use crate::annotation::find_type_def_symbols;
|
||||||
use crate::annotation::IntroducedVariables;
|
use crate::annotation::IntroducedVariables;
|
||||||
|
use crate::annotation::OwnedNamedOrAble;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::expr::AnnotatedMark;
|
use crate::expr::AnnotatedMark;
|
||||||
use crate::expr::ClosureData;
|
use crate::expr::ClosureData;
|
||||||
|
@ -29,6 +30,7 @@ use roc_problem::can::{CycleEntry, Problem, RuntimeError};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::AliasKind;
|
use roc_types::types::AliasKind;
|
||||||
|
use roc_types::types::AliasVar;
|
||||||
use roc_types::types::LambdaSet;
|
use roc_types::types::LambdaSet;
|
||||||
use roc_types::types::{Alias, Type};
|
use roc_types::types::{Alias, Type};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -318,38 +320,43 @@ pub(crate) fn canonicalize_defs<'a>(
|
||||||
&abilities_in_scope,
|
&abilities_in_scope,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Does this alias reference any abilities? For now, we don't permit that.
|
|
||||||
let ability_references = can_ann
|
|
||||||
.references
|
|
||||||
.iter()
|
|
||||||
.filter_map(|&ty_ref| abilities_in_scope.iter().find(|&&name| name == ty_ref))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if let Some(one_ability_ref) = ability_references.first() {
|
|
||||||
env.problem(Problem::AliasUsesAbility {
|
|
||||||
loc_name: name,
|
|
||||||
ability: **one_ability_ref,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record all the annotation's references in output.references.lookups
|
// Record all the annotation's references in output.references.lookups
|
||||||
for symbol in can_ann.references {
|
for symbol in can_ann.references {
|
||||||
output.references.insert_type_lookup(symbol);
|
output.references.insert_type_lookup(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut can_vars: Vec<Loc<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
|
let mut can_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(vars.len());
|
||||||
let mut is_phantom = false;
|
let mut is_phantom = false;
|
||||||
|
|
||||||
let mut named = can_ann.introduced_variables.named;
|
let IntroducedVariables {
|
||||||
|
named,
|
||||||
|
able,
|
||||||
|
wildcards,
|
||||||
|
inferred,
|
||||||
|
..
|
||||||
|
} = can_ann.introduced_variables;
|
||||||
|
|
||||||
|
let mut named: Vec<_> = (named.into_iter().map(OwnedNamedOrAble::Named))
|
||||||
|
.chain(able.into_iter().map(OwnedNamedOrAble::Able))
|
||||||
|
.collect();
|
||||||
for loc_lowercase in vars.iter() {
|
for loc_lowercase in vars.iter() {
|
||||||
let opt_index = named.iter().position(|nv| nv.name == loc_lowercase.value);
|
let opt_index = named
|
||||||
|
.iter()
|
||||||
|
.position(|nv| nv.ref_name() == &loc_lowercase.value);
|
||||||
match opt_index {
|
match opt_index {
|
||||||
Some(index) => {
|
Some(index) => {
|
||||||
// This is a valid lowercase rigid var for the type def.
|
// This is a valid lowercase rigid var for the type def.
|
||||||
let named_variable = named.swap_remove(index);
|
let named_variable = named.swap_remove(index);
|
||||||
|
let var = named_variable.variable();
|
||||||
|
let opt_bound_ability = named_variable.opt_ability();
|
||||||
|
let name = named_variable.name();
|
||||||
|
|
||||||
can_vars.push(Loc {
|
can_vars.push(Loc {
|
||||||
value: (named_variable.name, named_variable.variable),
|
value: AliasVar {
|
||||||
|
name,
|
||||||
|
var,
|
||||||
|
opt_bound_ability,
|
||||||
|
},
|
||||||
region: loc_lowercase.region,
|
region: loc_lowercase.region,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -370,16 +377,11 @@ pub(crate) fn canonicalize_defs<'a>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let IntroducedVariables {
|
|
||||||
wildcards,
|
|
||||||
inferred,
|
|
||||||
..
|
|
||||||
} = can_ann.introduced_variables;
|
|
||||||
let num_unbound = named.len() + wildcards.len() + inferred.len();
|
let num_unbound = named.len() + wildcards.len() + inferred.len();
|
||||||
if num_unbound > 0 {
|
if num_unbound > 0 {
|
||||||
let one_occurrence = named
|
let one_occurrence = named
|
||||||
.iter()
|
.iter()
|
||||||
.map(|nv| Loc::at(nv.first_seen, nv.variable))
|
.map(|nv| Loc::at(nv.first_seen(), nv.variable()))
|
||||||
.chain(wildcards)
|
.chain(wildcards)
|
||||||
.chain(inferred)
|
.chain(inferred)
|
||||||
.next()
|
.next()
|
||||||
|
@ -578,8 +580,10 @@ fn resolve_abilities<'a>(
|
||||||
|
|
||||||
// What variables in the annotation are bound to the parent ability, and what variables
|
// What variables in the annotation are bound to the parent ability, and what variables
|
||||||
// are bound to some other ability?
|
// are bound to some other ability?
|
||||||
let (variables_bound_to_ability, variables_bound_to_other_abilities): (Vec<_>, Vec<_>) =
|
let (variables_bound_to_ability, _variables_bound_to_other_abilities): (
|
||||||
member_annot
|
Vec<_>,
|
||||||
|
Vec<_>,
|
||||||
|
) = member_annot
|
||||||
.introduced_variables
|
.introduced_variables
|
||||||
.able
|
.able
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -618,18 +622,6 @@ fn resolve_abilities<'a>(
|
||||||
bad_has_clauses = true;
|
bad_has_clauses = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !variables_bound_to_other_abilities.is_empty() {
|
|
||||||
// Disallow variables bound to other abilities, for now.
|
|
||||||
for bad_variable in variables_bound_to_other_abilities.iter() {
|
|
||||||
env.problem(Problem::AbilityMemberBindsExternalAbility {
|
|
||||||
member: member_sym,
|
|
||||||
ability: loc_ability_name.value,
|
|
||||||
region: bad_variable.first_seen,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
bad_has_clauses = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if bad_has_clauses {
|
if bad_has_clauses {
|
||||||
// Pretend the member isn't a part of the ability
|
// Pretend the member isn't a part of the ability
|
||||||
continue;
|
continue;
|
||||||
|
@ -1740,7 +1732,7 @@ fn make_tag_union_of_alias_recursive<'a>(
|
||||||
let alias_args = alias
|
let alias_args = alias
|
||||||
.type_variables
|
.type_variables
|
||||||
.iter()
|
.iter()
|
||||||
.map(|l| (l.value.0.clone(), Type::Variable(l.value.1)))
|
.map(|l| (l.value.name.clone(), Type::Variable(l.value.var)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let made_recursive = make_tag_union_recursive_help(
|
let made_recursive = make_tag_union_recursive_help(
|
||||||
|
@ -1847,10 +1839,17 @@ fn make_tag_union_recursive_help<'a, 'b>(
|
||||||
type_arguments,
|
type_arguments,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
// NB: We need to collect the type arguments to shut off rustc's closure type
|
||||||
|
// instantiator. Otherwise we get unfortunate errors like
|
||||||
|
// reached the recursion limit while instantiating `make_tag_union_recursive_help::<...n/src/def.rs:1879:65: 1879:77]>>`
|
||||||
|
#[allow(clippy::needless_collect)]
|
||||||
|
let type_arguments: Vec<&Type> = type_arguments.iter().map(|ta| &ta.typ).collect();
|
||||||
|
let recursive_alias = Loc::at_zero((symbol, type_arguments.into_iter()));
|
||||||
|
|
||||||
// try to make `actual` recursive
|
// try to make `actual` recursive
|
||||||
make_tag_union_recursive_help(
|
make_tag_union_recursive_help(
|
||||||
env,
|
env,
|
||||||
Loc::at_zero((symbol, type_arguments.iter())),
|
recursive_alias,
|
||||||
region,
|
region,
|
||||||
others,
|
others,
|
||||||
actual,
|
actual,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
||||||
use roc_types::types::{AliasKind, LambdaSet, Type, TypeExtension};
|
use roc_types::types::{AliasKind, LambdaSet, OptAbleType, OptAbleVar, Type, TypeExtension};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub(crate) struct HostedGeneratedFunctions {
|
pub(crate) struct HostedGeneratedFunctions {
|
||||||
|
@ -1007,7 +1007,7 @@ fn build_effect_loop(
|
||||||
|
|
||||||
Type::Alias {
|
Type::Alias {
|
||||||
symbol: effect_symbol,
|
symbol: effect_symbol,
|
||||||
type_arguments: vec![state_type],
|
type_arguments: vec![OptAbleType::unbound(state_type)],
|
||||||
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(
|
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(
|
||||||
closure_var,
|
closure_var,
|
||||||
))],
|
))],
|
||||||
|
@ -1445,7 +1445,7 @@ fn build_effect_opaque(
|
||||||
|
|
||||||
Type::Alias {
|
Type::Alias {
|
||||||
symbol: effect_symbol,
|
symbol: effect_symbol,
|
||||||
type_arguments: vec![Type::Variable(a_var)],
|
type_arguments: vec![OptAbleType::unbound(Type::Variable(a_var))],
|
||||||
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))],
|
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))],
|
||||||
actual: Box::new(actual),
|
actual: Box::new(actual),
|
||||||
kind: AliasKind::Opaque,
|
kind: AliasKind::Opaque,
|
||||||
|
@ -1454,7 +1454,7 @@ fn build_effect_opaque(
|
||||||
|
|
||||||
fn build_fresh_opaque_variables(
|
fn build_fresh_opaque_variables(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> (Box<Type>, Vec<Variable>, Vec<LambdaSet>) {
|
) -> (Box<Type>, Vec<OptAbleVar>, Vec<LambdaSet>) {
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
|
|
||||||
// NB: if there are bugs, check whether not introducing variables is a problem!
|
// NB: if there are bugs, check whether not introducing variables is a problem!
|
||||||
|
@ -1466,7 +1466,10 @@ fn build_fresh_opaque_variables(
|
||||||
Box::new(Type::Variable(closure_var)),
|
Box::new(Type::Variable(closure_var)),
|
||||||
Box::new(Type::Variable(a_var)),
|
Box::new(Type::Variable(a_var)),
|
||||||
);
|
);
|
||||||
let type_arguments = vec![a_var];
|
let type_arguments = vec![OptAbleVar {
|
||||||
|
var: a_var,
|
||||||
|
opt_ability: None,
|
||||||
|
}];
|
||||||
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
|
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
|
||||||
|
|
||||||
(Box::new(actual), type_arguments, lambda_set_variables)
|
(Box::new(actual), type_arguments, lambda_set_variables)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::abilities::SpecializationId;
|
||||||
use crate::annotation::{freshen_opaque_def, IntroducedVariables};
|
use crate::annotation::{freshen_opaque_def, IntroducedVariables};
|
||||||
use crate::builtins::builtin_defs_map;
|
use crate::builtins::builtin_defs_map;
|
||||||
use crate::def::{can_defs_with_return, Def};
|
use crate::def::{can_defs_with_return, Def};
|
||||||
|
@ -19,7 +20,7 @@ use roc_parse::pattern::PatternType::*;
|
||||||
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
||||||
use roc_types::types::{Alias, Category, LambdaSet, Type};
|
use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type};
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::{char, u32};
|
use std::{char, u32};
|
||||||
|
|
||||||
|
@ -83,6 +84,13 @@ pub enum Expr {
|
||||||
|
|
||||||
// Lookups
|
// Lookups
|
||||||
Var(Symbol),
|
Var(Symbol),
|
||||||
|
AbilityMember(
|
||||||
|
/// Actual member name
|
||||||
|
Symbol,
|
||||||
|
/// Specialization to use
|
||||||
|
SpecializationId,
|
||||||
|
),
|
||||||
|
|
||||||
// Branching
|
// Branching
|
||||||
When {
|
When {
|
||||||
/// The actual condition of the when expression.
|
/// The actual condition of the when expression.
|
||||||
|
@ -191,7 +199,7 @@ pub enum Expr {
|
||||||
// for the expression from the opaque definition. `type_arguments` is something like
|
// for the expression from the opaque definition. `type_arguments` is something like
|
||||||
// [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]".
|
// [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]".
|
||||||
specialized_def_type: Box<Type>,
|
specialized_def_type: Box<Type>,
|
||||||
type_arguments: Vec<Variable>,
|
type_arguments: Vec<OptAbleVar>,
|
||||||
lambda_set_variables: Vec<LambdaSet>,
|
lambda_set_variables: Vec<LambdaSet>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -212,6 +220,7 @@ impl Expr {
|
||||||
Self::SingleQuote(..) => Category::Character,
|
Self::SingleQuote(..) => Category::Character,
|
||||||
Self::List { .. } => Category::List,
|
Self::List { .. } => Category::List,
|
||||||
&Self::Var(sym) => Category::Lookup(sym),
|
&Self::Var(sym) => Category::Lookup(sym),
|
||||||
|
&Self::AbilityMember(sym, _) => Category::Lookup(sym),
|
||||||
Self::When { .. } => Category::When,
|
Self::When { .. } => Category::When,
|
||||||
Self::If { .. } => Category::If,
|
Self::If { .. } => Category::If,
|
||||||
Self::LetRec(_, expr) => expr.value.category(),
|
Self::LetRec(_, expr) => expr.value.category(),
|
||||||
|
@ -390,6 +399,17 @@ impl WhenBranch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WhenBranch {
|
||||||
|
pub fn region(&self) -> Region {
|
||||||
|
Region::across_all(
|
||||||
|
self.patterns
|
||||||
|
.iter()
|
||||||
|
.map(|p| &p.region)
|
||||||
|
.chain([self.value.region].iter()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn canonicalize_expr<'a>(
|
pub fn canonicalize_expr<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
@ -1307,8 +1327,12 @@ fn canonicalize_var_lookup(
|
||||||
Ok(symbol) => {
|
Ok(symbol) => {
|
||||||
output.references.insert_value_lookup(symbol);
|
output.references.insert_value_lookup(symbol);
|
||||||
|
|
||||||
|
if scope.abilities_store.is_ability_member_name(symbol) {
|
||||||
|
AbilityMember(symbol, scope.abilities_store.fresh_specialization_id())
|
||||||
|
} else {
|
||||||
Var(symbol)
|
Var(symbol)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Err(problem) => {
|
Err(problem) => {
|
||||||
env.problem(Problem::RuntimeError(problem.clone()));
|
env.problem(Problem::RuntimeError(problem.clone()));
|
||||||
|
|
||||||
|
@ -1322,8 +1346,12 @@ fn canonicalize_var_lookup(
|
||||||
Ok(symbol) => {
|
Ok(symbol) => {
|
||||||
output.references.insert_value_lookup(symbol);
|
output.references.insert_value_lookup(symbol);
|
||||||
|
|
||||||
|
if scope.abilities_store.is_ability_member_name(symbol) {
|
||||||
|
AbilityMember(symbol, scope.abilities_store.fresh_specialization_id())
|
||||||
|
} else {
|
||||||
Var(symbol)
|
Var(symbol)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Err(problem) => {
|
Err(problem) => {
|
||||||
// Either the module wasn't imported, or
|
// Either the module wasn't imported, or
|
||||||
// it was imported but it doesn't expose this ident.
|
// it was imported but it doesn't expose this ident.
|
||||||
|
@ -1356,6 +1384,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
| other @ Accessor { .. }
|
| other @ Accessor { .. }
|
||||||
| other @ Update { .. }
|
| other @ Update { .. }
|
||||||
| other @ Var(_)
|
| other @ Var(_)
|
||||||
|
| other @ AbilityMember(..)
|
||||||
| other @ RunLowLevel { .. }
|
| other @ RunLowLevel { .. }
|
||||||
| other @ ForeignCall { .. } => other,
|
| other @ ForeignCall { .. } => other,
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use roc_parse::pattern::PatternType;
|
||||||
use roc_problem::can::{Problem, RuntimeError};
|
use roc_problem::can::{Problem, RuntimeError};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{Alias, AliasKind, Type};
|
use roc_types::types::{Alias, AliasKind, AliasVar, Type};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
|
@ -119,7 +119,7 @@ impl GeneratedInfo {
|
||||||
scope.add_alias(
|
scope.add_alias(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
Region::zero(),
|
Region::zero(),
|
||||||
vec![Loc::at_zero(("a".into(), a_var))],
|
vec![Loc::at_zero(AliasVar::unbound("a".into(), a_var))],
|
||||||
actual,
|
actual,
|
||||||
AliasKind::Opaque,
|
AliasKind::Opaque,
|
||||||
);
|
);
|
||||||
|
@ -704,6 +704,7 @@ fn fix_values_captured_in_closure_expr(
|
||||||
| Str(_)
|
| Str(_)
|
||||||
| SingleQuote(_)
|
| SingleQuote(_)
|
||||||
| Var(_)
|
| Var(_)
|
||||||
|
| AbilityMember(..)
|
||||||
| EmptyRecord
|
| EmptyRecord
|
||||||
| RuntimeError(_)
|
| RuntimeError(_)
|
||||||
| ZeroArgumentTag { .. }
|
| ZeroArgumentTag { .. }
|
||||||
|
|
|
@ -13,7 +13,7 @@ use roc_parse::pattern::PatternType;
|
||||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
|
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{LambdaSet, PatternCategory, Type};
|
use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type};
|
||||||
|
|
||||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||||
/// codegen can generate a runtime error if this pattern is reached.
|
/// codegen can generate a runtime error if this pattern is reached.
|
||||||
|
@ -47,7 +47,7 @@ pub enum Pattern {
|
||||||
// for the expression from the opaque definition. `type_arguments` is something like
|
// for the expression from the opaque definition. `type_arguments` is something like
|
||||||
// [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]".
|
// [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]".
|
||||||
specialized_def_type: Box<Type>,
|
specialized_def_type: Box<Type>,
|
||||||
type_arguments: Vec<Variable>,
|
type_arguments: Vec<OptAbleVar>,
|
||||||
lambda_set_variables: Vec<LambdaSet>,
|
lambda_set_variables: Vec<LambdaSet>,
|
||||||
},
|
},
|
||||||
RecordDestructure {
|
RecordDestructure {
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use roc_collections::VecMap;
|
use roc_collections::VecMap;
|
||||||
use roc_module::ident::{Ident, Lowercase};
|
use roc_module::ident::Ident;
|
||||||
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
|
||||||
use roc_problem::can::RuntimeError;
|
use roc_problem::can::RuntimeError;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::types::{Alias, AliasKind, AliasVar, Type};
|
||||||
use roc_types::types::{Alias, AliasKind, Type};
|
|
||||||
|
|
||||||
use crate::abilities::AbilitiesStore;
|
use crate::abilities::AbilitiesStore;
|
||||||
|
|
||||||
|
@ -335,7 +334,7 @@ impl Scope {
|
||||||
&mut self,
|
&mut self,
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
vars: Vec<Loc<(Lowercase, Variable)>>,
|
vars: Vec<Loc<AliasVar>>,
|
||||||
typ: Type,
|
typ: Type,
|
||||||
kind: AliasKind,
|
kind: AliasKind,
|
||||||
) {
|
) {
|
||||||
|
@ -394,7 +393,7 @@ impl Scope {
|
||||||
pub fn create_alias(
|
pub fn create_alias(
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
vars: Vec<Loc<(Lowercase, Variable)>>,
|
vars: Vec<Loc<AliasVar>>,
|
||||||
typ: Type,
|
typ: Type,
|
||||||
kind: AliasKind,
|
kind: AliasKind,
|
||||||
) -> Alias {
|
) -> Alias {
|
||||||
|
@ -408,7 +407,7 @@ pub fn create_alias(
|
||||||
let mut hidden = type_variables;
|
let mut hidden = type_variables;
|
||||||
|
|
||||||
for loc_var in vars.iter() {
|
for loc_var in vars.iter() {
|
||||||
hidden.remove(&loc_var.value.1);
|
hidden.remove(&loc_var.value.var);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hidden.is_empty() {
|
if !hidden.is_empty() {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
//! Traversals over the can ast.
|
//! Traversals over the can ast.
|
||||||
|
|
||||||
|
use roc_module::ident::Lowercase;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
def::{Annotation, Declaration, Def},
|
def::{Annotation, Declaration, Def},
|
||||||
expr::{ClosureData, Expr, WhenBranch},
|
expr::{AccessorData, ClosureData, Expr, Field, WhenBranch},
|
||||||
pattern::Pattern,
|
pattern::Pattern,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,11 +18,11 @@ macro_rules! visit_list {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_decls<V: Visitor>(visitor: &mut V, decls: &[Declaration]) {
|
pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &[Declaration]) {
|
||||||
visit_list!(visitor, visit_decl, decls)
|
visit_list!(visitor, visit_decl, decls)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_decl<V: Visitor>(visitor: &mut V, decl: &Declaration) {
|
pub fn walk_decl<V: Visitor>(visitor: &mut V, decl: &Declaration) {
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::Declare(def) => {
|
Declaration::Declare(def) => {
|
||||||
visitor.visit_def(def);
|
visitor.visit_def(def);
|
||||||
|
@ -31,12 +32,12 @@ fn walk_decl<V: Visitor>(visitor: &mut V, decl: &Declaration) {
|
||||||
}
|
}
|
||||||
Declaration::Builtin(def) => visitor.visit_def(def),
|
Declaration::Builtin(def) => visitor.visit_def(def),
|
||||||
Declaration::InvalidCycle(_cycles) => {
|
Declaration::InvalidCycle(_cycles) => {
|
||||||
todo!()
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_def<V: Visitor>(visitor: &mut V, def: &Def) {
|
pub fn walk_def<V: Visitor>(visitor: &mut V, def: &Def) {
|
||||||
let Def {
|
let Def {
|
||||||
loc_pattern,
|
loc_pattern,
|
||||||
loc_expr,
|
loc_expr,
|
||||||
|
@ -45,18 +46,19 @@ fn walk_def<V: Visitor>(visitor: &mut V, def: &Def) {
|
||||||
..
|
..
|
||||||
} = def;
|
} = def;
|
||||||
|
|
||||||
visitor.visit_pattern(
|
let opt_var = match loc_pattern.value {
|
||||||
&loc_pattern.value,
|
Pattern::Identifier(..) | Pattern::AbilityMemberSpecialization { .. } => Some(*expr_var),
|
||||||
loc_pattern.region,
|
_ => loc_pattern.value.opt_var(),
|
||||||
loc_pattern.value.opt_var(),
|
};
|
||||||
);
|
|
||||||
|
visitor.visit_pattern(&loc_pattern.value, loc_pattern.region, opt_var);
|
||||||
visitor.visit_expr(&loc_expr.value, loc_expr.region, *expr_var);
|
visitor.visit_expr(&loc_expr.value, loc_expr.region, *expr_var);
|
||||||
if let Some(annot) = &annotation {
|
if let Some(annot) = &annotation {
|
||||||
visitor.visit_annotation(annot);
|
visitor.visit_annotation(annot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
|
pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Closure(closure_data) => walk_closure(visitor, closure_data),
|
Expr::Closure(closure_data) => walk_closure(visitor, closure_data),
|
||||||
Expr::When {
|
Expr::When {
|
||||||
|
@ -70,11 +72,107 @@ fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
|
||||||
} => {
|
} => {
|
||||||
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
|
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
|
||||||
}
|
}
|
||||||
e => todo!("{:?}", e),
|
Expr::Num(..) => { /* terminal */ }
|
||||||
|
Expr::Int(..) => { /* terminal */ }
|
||||||
|
Expr::Float(..) => { /* terminal */ }
|
||||||
|
Expr::Str(..) => { /* terminal */ }
|
||||||
|
Expr::SingleQuote(..) => { /* terminal */ }
|
||||||
|
Expr::List {
|
||||||
|
elem_var,
|
||||||
|
loc_elems,
|
||||||
|
} => {
|
||||||
|
walk_list(visitor, *elem_var, loc_elems);
|
||||||
|
}
|
||||||
|
Expr::Var(..) => { /* terminal */ }
|
||||||
|
Expr::AbilityMember(..) => { /* terminal */ }
|
||||||
|
Expr::If {
|
||||||
|
cond_var,
|
||||||
|
branches,
|
||||||
|
branch_var,
|
||||||
|
final_else,
|
||||||
|
} => walk_if(visitor, *cond_var, branches, *branch_var, final_else),
|
||||||
|
Expr::LetRec(defs, body) => {
|
||||||
|
defs.iter().for_each(|def| visitor.visit_def(def));
|
||||||
|
visitor.visit_expr(&body.value, body.region, var);
|
||||||
|
}
|
||||||
|
Expr::LetNonRec(def, body) => {
|
||||||
|
visitor.visit_def(def);
|
||||||
|
visitor.visit_expr(&body.value, body.region, var);
|
||||||
|
}
|
||||||
|
Expr::Call(f, args, _called_via) => {
|
||||||
|
let (fn_var, loc_fn, _closure_var, _ret_var) = &**f;
|
||||||
|
walk_call(visitor, *fn_var, loc_fn, args);
|
||||||
|
}
|
||||||
|
Expr::RunLowLevel {
|
||||||
|
op: _,
|
||||||
|
args,
|
||||||
|
ret_var: _,
|
||||||
|
} => {
|
||||||
|
args.iter()
|
||||||
|
.for_each(|(v, e)| visitor.visit_expr(e, Region::zero(), *v));
|
||||||
|
}
|
||||||
|
Expr::ForeignCall {
|
||||||
|
foreign_symbol: _,
|
||||||
|
args,
|
||||||
|
ret_var: _,
|
||||||
|
} => {
|
||||||
|
args.iter()
|
||||||
|
.for_each(|(v, e)| visitor.visit_expr(e, Region::zero(), *v));
|
||||||
|
}
|
||||||
|
Expr::Record {
|
||||||
|
record_var: _,
|
||||||
|
fields,
|
||||||
|
} => {
|
||||||
|
walk_record_fields(visitor, fields.iter());
|
||||||
|
}
|
||||||
|
Expr::EmptyRecord => { /* terminal */ }
|
||||||
|
Expr::Access {
|
||||||
|
field_var,
|
||||||
|
loc_expr,
|
||||||
|
field: _,
|
||||||
|
record_var: _,
|
||||||
|
ext_var: _,
|
||||||
|
} => visitor.visit_expr(&loc_expr.value, loc_expr.region, *field_var),
|
||||||
|
Expr::Accessor(AccessorData { .. }) => { /* terminal */ }
|
||||||
|
Expr::Update {
|
||||||
|
record_var: _,
|
||||||
|
ext_var: _,
|
||||||
|
symbol: _,
|
||||||
|
updates,
|
||||||
|
} => {
|
||||||
|
walk_record_fields(visitor, updates.iter());
|
||||||
|
}
|
||||||
|
Expr::Tag {
|
||||||
|
variant_var: _,
|
||||||
|
ext_var: _,
|
||||||
|
name: _,
|
||||||
|
arguments,
|
||||||
|
} => arguments
|
||||||
|
.iter()
|
||||||
|
.for_each(|(v, le)| visitor.visit_expr(&le.value, le.region, *v)),
|
||||||
|
Expr::ZeroArgumentTag { .. } => { /* terminal */ }
|
||||||
|
Expr::OpaqueRef {
|
||||||
|
opaque_var: _,
|
||||||
|
name: _,
|
||||||
|
argument,
|
||||||
|
specialized_def_type: _,
|
||||||
|
type_arguments: _,
|
||||||
|
lambda_set_variables: _,
|
||||||
|
} => {
|
||||||
|
let (var, le) = &**argument;
|
||||||
|
visitor.visit_expr(&le.value, le.region, *var);
|
||||||
|
}
|
||||||
|
Expr::Expect(e1, e2) => {
|
||||||
|
// TODO: what type does an expect have?
|
||||||
|
visitor.visit_expr(&e1.value, e1.region, Variable::NULL);
|
||||||
|
visitor.visit_expr(&e2.value, e2.region, Variable::NULL);
|
||||||
|
}
|
||||||
|
Expr::RuntimeError(..) => { /* terminal */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_closure<V: Visitor>(visitor: &mut V, clos: &ClosureData) {
|
#[inline(always)]
|
||||||
|
pub fn walk_closure<V: Visitor>(visitor: &mut V, clos: &ClosureData) {
|
||||||
let ClosureData {
|
let ClosureData {
|
||||||
arguments,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
|
@ -89,7 +187,8 @@ fn walk_closure<V: Visitor>(visitor: &mut V, clos: &ClosureData) {
|
||||||
visitor.visit_expr(&loc_body.value, loc_body.region, *return_type);
|
visitor.visit_expr(&loc_body.value, loc_body.region, *return_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_when<V: Visitor>(
|
#[inline(always)]
|
||||||
|
pub fn walk_when<V: Visitor>(
|
||||||
visitor: &mut V,
|
visitor: &mut V,
|
||||||
cond_var: Variable,
|
cond_var: Variable,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
|
@ -103,7 +202,8 @@ fn walk_when<V: Visitor>(
|
||||||
.for_each(|branch| walk_when_branch(visitor, branch, expr_var));
|
.for_each(|branch| walk_when_branch(visitor, branch, expr_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_when_branch<V: Visitor>(visitor: &mut V, branch: &WhenBranch, expr_var: Variable) {
|
#[inline(always)]
|
||||||
|
pub fn walk_when_branch<V: Visitor>(visitor: &mut V, branch: &WhenBranch, expr_var: Variable) {
|
||||||
let WhenBranch {
|
let WhenBranch {
|
||||||
patterns,
|
patterns,
|
||||||
value,
|
value,
|
||||||
|
@ -120,11 +220,58 @@ fn walk_when_branch<V: Visitor>(visitor: &mut V, branch: &WhenBranch, expr_var:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_pattern<V: Visitor>(_visitor: &mut V, _pat: &Pattern) {
|
#[inline(always)]
|
||||||
todo!()
|
pub fn walk_list<V: Visitor>(visitor: &mut V, elem_var: Variable, loc_elems: &[Loc<Expr>]) {
|
||||||
|
loc_elems
|
||||||
|
.iter()
|
||||||
|
.for_each(|le| visitor.visit_expr(&le.value, le.region, elem_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Visitor: Sized {
|
#[inline(always)]
|
||||||
|
pub fn walk_if<V: Visitor>(
|
||||||
|
visitor: &mut V,
|
||||||
|
cond_var: Variable,
|
||||||
|
branches: &[(Loc<Expr>, Loc<Expr>)],
|
||||||
|
branch_var: Variable,
|
||||||
|
final_else: &Loc<Expr>,
|
||||||
|
) {
|
||||||
|
branches.iter().for_each(|(cond, body)| {
|
||||||
|
visitor.visit_expr(&cond.value, cond.region, cond_var);
|
||||||
|
visitor.visit_expr(&body.value, body.region, branch_var);
|
||||||
|
});
|
||||||
|
visitor.visit_expr(&final_else.value, final_else.region, branch_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn walk_call<V: Visitor>(
|
||||||
|
visitor: &mut V,
|
||||||
|
fn_var: Variable,
|
||||||
|
fn_expr: &Loc<Expr>,
|
||||||
|
args: &[(Variable, Loc<Expr>)],
|
||||||
|
) {
|
||||||
|
visitor.visit_expr(&fn_expr.value, fn_expr.region, fn_var);
|
||||||
|
args.iter()
|
||||||
|
.for_each(|(v, le)| visitor.visit_expr(&le.value, le.region, *v));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn walk_record_fields<'a, V: Visitor>(
|
||||||
|
visitor: &mut V,
|
||||||
|
fields: impl Iterator<Item = (&'a Lowercase, &'a Field)>,
|
||||||
|
) {
|
||||||
|
fields.for_each(
|
||||||
|
|(
|
||||||
|
_name,
|
||||||
|
Field {
|
||||||
|
var,
|
||||||
|
loc_expr,
|
||||||
|
region: _,
|
||||||
|
},
|
||||||
|
)| { visitor.visit_expr(&loc_expr.value, loc_expr.region, *var) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Visitor: Sized + PatternVisitor {
|
||||||
fn visit_decls(&mut self, decls: &[Declaration]) {
|
fn visit_decls(&mut self, decls: &[Declaration]) {
|
||||||
walk_decls(self, decls);
|
walk_decls(self, decls);
|
||||||
}
|
}
|
||||||
|
@ -137,16 +284,22 @@ trait Visitor: Sized {
|
||||||
walk_def(self, def);
|
walk_def(self, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_pattern(&mut self, pat: &Pattern, _region: Region, _opt_var: Option<Variable>) {
|
|
||||||
walk_pattern(self, pat)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_annotation(&mut self, _pat: &Annotation) {
|
fn visit_annotation(&mut self, _pat: &Annotation) {
|
||||||
// TODO
|
// ignore by default
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &Expr, _region: Region, _var: Variable) {
|
fn visit_expr(&mut self, expr: &Expr, _region: Region, var: Variable) {
|
||||||
walk_expr(self, expr);
|
walk_expr(self, expr, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn walk_pattern<V: PatternVisitor>(_visitor: &mut V, _pattern: &Pattern) {
|
||||||
|
// ignore for now
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PatternVisitor: Sized {
|
||||||
|
fn visit_pattern(&mut self, pattern: &Pattern, _region: Region, _opt_var: Option<Variable>) {
|
||||||
|
walk_pattern(self, pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,18 +308,7 @@ struct TypeAtVisitor {
|
||||||
typ: Option<Variable>,
|
typ: Option<Variable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visitor for TypeAtVisitor {
|
impl PatternVisitor for TypeAtVisitor {
|
||||||
fn visit_expr(&mut self, expr: &Expr, region: Region, var: Variable) {
|
|
||||||
if region == self.region {
|
|
||||||
debug_assert!(self.typ.is_none());
|
|
||||||
self.typ = Some(var);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if region.contains(&self.region) {
|
|
||||||
walk_expr(self, expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_pattern(&mut self, pat: &Pattern, region: Region, opt_var: Option<Variable>) {
|
fn visit_pattern(&mut self, pat: &Pattern, region: Region, opt_var: Option<Variable>) {
|
||||||
if region == self.region {
|
if region == self.region {
|
||||||
debug_assert!(self.typ.is_none());
|
debug_assert!(self.typ.is_none());
|
||||||
|
@ -178,6 +320,18 @@ impl Visitor for TypeAtVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Visitor for TypeAtVisitor {
|
||||||
|
fn visit_expr(&mut self, expr: &Expr, region: Region, var: Variable) {
|
||||||
|
if region == self.region {
|
||||||
|
debug_assert!(self.typ.is_none());
|
||||||
|
self.typ = Some(var);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if region.contains(&self.region) {
|
||||||
|
walk_expr(self, expr, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to find the type of an expression at `region`, if it exists.
|
/// Attempts to find the type of an expression at `region`, if it exists.
|
||||||
pub fn find_type_at(region: Region, decls: &[Declaration]) -> Option<Variable> {
|
pub fn find_type_at(region: Region, decls: &[Declaration]) -> Option<Variable> {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_problem::can::Problem;
|
use roc_problem::can::Problem;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::Type;
|
use roc_types::types::{AliasVar, Type};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
pub fn test_home() -> ModuleId {
|
pub fn test_home() -> ModuleId {
|
||||||
|
@ -59,7 +59,10 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
||||||
scope.add_alias(
|
scope.add_alias(
|
||||||
Symbol::NUM_INT,
|
Symbol::NUM_INT,
|
||||||
Region::zero(),
|
Region::zero(),
|
||||||
vec![Loc::at_zero(("a".into(), Variable::EMPTY_RECORD))],
|
vec![Loc::at_zero(AliasVar::unbound(
|
||||||
|
"a".into(),
|
||||||
|
Variable::EMPTY_RECORD,
|
||||||
|
))],
|
||||||
Type::EmptyRec,
|
Type::EmptyRec,
|
||||||
roc_types::types::AliasKind::Structural,
|
roc_types::types::AliasKind::Structural,
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,9 +5,9 @@ use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, Sig
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::Reason;
|
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{AliasKind, Category};
|
use roc_types::types::{AliasKind, Category};
|
||||||
|
use roc_types::types::{OptAbleType, Reason};
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -160,7 +160,7 @@ pub fn str_type() -> Type {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn builtin_alias(
|
fn builtin_alias(
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
type_arguments: Vec<Type>,
|
type_arguments: Vec<OptAbleType>,
|
||||||
actual: Box<Type>,
|
actual: Box<Type>,
|
||||||
kind: AliasKind,
|
kind: AliasKind,
|
||||||
) -> Type {
|
) -> Type {
|
||||||
|
@ -177,7 +177,7 @@ fn builtin_alias(
|
||||||
pub fn num_float(range: Type) -> Type {
|
pub fn num_float(range: Type) -> Type {
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_FLOAT,
|
Symbol::NUM_FLOAT,
|
||||||
vec![range.clone()],
|
vec![OptAbleType::unbound(range.clone())],
|
||||||
Box::new(num_num(num_floatingpoint(range))),
|
Box::new(num_num(num_floatingpoint(range))),
|
||||||
AliasKind::Structural,
|
AliasKind::Structural,
|
||||||
)
|
)
|
||||||
|
@ -187,7 +187,7 @@ pub fn num_float(range: Type) -> Type {
|
||||||
pub fn num_floatingpoint(range: Type) -> Type {
|
pub fn num_floatingpoint(range: Type) -> Type {
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
vec![range.clone()],
|
vec![OptAbleType::unbound(range.clone())],
|
||||||
Box::new(range),
|
Box::new(range),
|
||||||
AliasKind::Opaque,
|
AliasKind::Opaque,
|
||||||
)
|
)
|
||||||
|
@ -227,7 +227,7 @@ pub fn num_binary64() -> Type {
|
||||||
pub fn num_int(range: Type) -> Type {
|
pub fn num_int(range: Type) -> Type {
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_INT,
|
Symbol::NUM_INT,
|
||||||
vec![range.clone()],
|
vec![OptAbleType::unbound(range.clone())],
|
||||||
Box::new(num_num(num_integer(range))),
|
Box::new(num_num(num_integer(range))),
|
||||||
AliasKind::Structural,
|
AliasKind::Structural,
|
||||||
)
|
)
|
||||||
|
@ -247,7 +247,7 @@ pub fn num_signed64() -> Type {
|
||||||
pub fn num_integer(range: Type) -> Type {
|
pub fn num_integer(range: Type) -> Type {
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
vec![range.clone()],
|
vec![OptAbleType::unbound(range.clone())],
|
||||||
Box::new(range),
|
Box::new(range),
|
||||||
AliasKind::Opaque,
|
AliasKind::Opaque,
|
||||||
)
|
)
|
||||||
|
@ -257,7 +257,7 @@ pub fn num_integer(range: Type) -> Type {
|
||||||
pub fn num_num(typ: Type) -> Type {
|
pub fn num_num(typ: Type) -> Type {
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_NUM,
|
Symbol::NUM_NUM,
|
||||||
vec![typ.clone()],
|
vec![OptAbleType::unbound(typ.clone())],
|
||||||
Box::new(typ),
|
Box::new(typ),
|
||||||
AliasKind::Opaque,
|
AliasKind::Opaque,
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,7 +18,7 @@ use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
AliasKind, AnnotationSource, Category, PReason, Reason, RecordField, TypeExtension,
|
AliasKind, AnnotationSource, Category, OptAbleType, PReason, Reason, RecordField, TypeExtension,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This is for constraining Defs
|
/// This is for constraining Defs
|
||||||
|
@ -258,7 +258,7 @@ pub fn constrain_expr(
|
||||||
let (fn_var, loc_fn, closure_var, ret_var) = &**boxed;
|
let (fn_var, loc_fn, closure_var, ret_var) = &**boxed;
|
||||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||||
// (foo) bar baz
|
// (foo) bar baz
|
||||||
let opt_symbol = if let Var(symbol) = loc_fn.value {
|
let opt_symbol = if let Var(symbol) | AbilityMember(symbol, _) = loc_fn.value {
|
||||||
Some(symbol)
|
Some(symbol)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -336,6 +336,11 @@ pub fn constrain_expr(
|
||||||
// make lookup constraint to lookup this symbol's type in the environment
|
// make lookup constraint to lookup this symbol's type in the environment
|
||||||
constraints.lookup(*symbol, expected, region)
|
constraints.lookup(*symbol, expected, region)
|
||||||
}
|
}
|
||||||
|
AbilityMember(symbol, _specialization) => {
|
||||||
|
// make lookup constraint to lookup this symbol's type in the environment
|
||||||
|
constraints.lookup(*symbol, expected, region)
|
||||||
|
// TODO: consider trying to solve `_specialization` here.
|
||||||
|
}
|
||||||
Closure(ClosureData {
|
Closure(ClosureData {
|
||||||
function_type: fn_var,
|
function_type: fn_var,
|
||||||
closure_type: closure_var,
|
closure_type: closure_var,
|
||||||
|
@ -998,7 +1003,13 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let opaque_type = Type::Alias {
|
let opaque_type = Type::Alias {
|
||||||
symbol: *name,
|
symbol: *name,
|
||||||
type_arguments: type_arguments.iter().copied().map(Type::Variable).collect(),
|
type_arguments: type_arguments
|
||||||
|
.iter()
|
||||||
|
.map(|v| OptAbleType {
|
||||||
|
typ: Type::Variable(v.var),
|
||||||
|
opt_ability: v.opt_ability,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
lambda_set_variables: lambda_set_variables.clone(),
|
lambda_set_variables: lambda_set_variables.clone(),
|
||||||
actual: Box::new(arg_type.clone()),
|
actual: Box::new(arg_type.clone()),
|
||||||
kind: AliasKind::Opaque,
|
kind: AliasKind::Opaque,
|
||||||
|
@ -1035,7 +1046,7 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let mut vars = vec![*arg_var, *opaque_var];
|
let mut vars = vec![*arg_var, *opaque_var];
|
||||||
// Also add the fresh variables we created for the type argument and lambda sets
|
// Also add the fresh variables we created for the type argument and lambda sets
|
||||||
vars.extend(type_arguments);
|
vars.extend(type_arguments.iter().map(|v| v.var));
|
||||||
vars.extend(lambda_set_variables.iter().map(|v| {
|
vars.extend(lambda_set_variables.iter().map(|v| {
|
||||||
v.0.expect_variable("all lambda sets should be fresh variables here")
|
v.0.expect_variable("all lambda sets should be fresh variables here")
|
||||||
}));
|
}));
|
||||||
|
@ -1471,8 +1482,8 @@ fn constrain_typed_def(
|
||||||
|
|
||||||
constrain_def_make_constraint(
|
constrain_def_make_constraint(
|
||||||
constraints,
|
constraints,
|
||||||
new_rigid_variables,
|
new_rigid_variables.into_iter(),
|
||||||
new_infer_variables,
|
new_infer_variables.into_iter(),
|
||||||
expr_con,
|
expr_con,
|
||||||
body_con,
|
body_con,
|
||||||
def_pattern_state,
|
def_pattern_state,
|
||||||
|
@ -1506,8 +1517,8 @@ fn constrain_typed_def(
|
||||||
|
|
||||||
constrain_def_make_constraint(
|
constrain_def_make_constraint(
|
||||||
constraints,
|
constraints,
|
||||||
new_rigid_variables,
|
new_rigid_variables.into_iter(),
|
||||||
new_infer_variables,
|
new_infer_variables.into_iter(),
|
||||||
expr_con,
|
expr_con,
|
||||||
body_con,
|
body_con,
|
||||||
def_pattern_state,
|
def_pattern_state,
|
||||||
|
@ -1672,8 +1683,8 @@ fn constrain_def(
|
||||||
|
|
||||||
constrain_def_make_constraint(
|
constrain_def_make_constraint(
|
||||||
constraints,
|
constraints,
|
||||||
vec![],
|
std::iter::empty(),
|
||||||
vec![],
|
std::iter::empty(),
|
||||||
expr_con,
|
expr_con,
|
||||||
body_con,
|
body_con,
|
||||||
def_pattern_state,
|
def_pattern_state,
|
||||||
|
@ -1684,8 +1695,8 @@ fn constrain_def(
|
||||||
|
|
||||||
pub fn constrain_def_make_constraint(
|
pub fn constrain_def_make_constraint(
|
||||||
constraints: &mut Constraints,
|
constraints: &mut Constraints,
|
||||||
new_rigid_variables: Vec<Variable>,
|
new_rigid_variables: impl Iterator<Item = Variable>,
|
||||||
new_infer_variables: Vec<Variable>,
|
new_infer_variables: impl Iterator<Item = Variable>,
|
||||||
expr_con: Constraint,
|
expr_con: Constraint,
|
||||||
body_con: Constraint,
|
body_con: Constraint,
|
||||||
def_pattern_state: PatternState,
|
def_pattern_state: PatternState,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env};
|
||||||
use roc_builtins::std::StdLib;
|
use roc_builtins::std::StdLib;
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::constraint::{Constraint, Constraints};
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
|
@ -12,8 +13,6 @@ use roc_types::solved_types::{FreeVars, SolvedType};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{AnnotationSource, Category, Type};
|
use roc_types::types::{AnnotationSource, Category, Type};
|
||||||
|
|
||||||
use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env};
|
|
||||||
|
|
||||||
/// The types of all exposed values/functions of a collection of modules
|
/// The types of all exposed values/functions of a collection of modules
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct ExposedByModule {
|
pub struct ExposedByModule {
|
||||||
|
@ -105,7 +104,7 @@ pub fn constrain_module(
|
||||||
let constraint = crate::expr::constrain_decls(constraints, home, declarations);
|
let constraint = crate::expr::constrain_decls(constraints, home, declarations);
|
||||||
let constraint =
|
let constraint =
|
||||||
constrain_symbols_from_requires(constraints, symbols_from_requires, home, constraint);
|
constrain_symbols_from_requires(constraints, symbols_from_requires, home, constraint);
|
||||||
let constraint = frontload_ability_constraints(constraints, abilities_store, constraint);
|
let constraint = frontload_ability_constraints(constraints, abilities_store, home, constraint);
|
||||||
|
|
||||||
// The module constraint should always save the environment at the end.
|
// The module constraint should always save the environment at the end.
|
||||||
debug_assert!(constraints.contains_save_the_environment(&constraint));
|
debug_assert!(constraints.contains_save_the_environment(&constraint));
|
||||||
|
@ -140,8 +139,8 @@ fn constrain_symbols_from_requires(
|
||||||
constraints,
|
constraints,
|
||||||
// No new rigids or flex vars because they are represented in the type
|
// No new rigids or flex vars because they are represented in the type
|
||||||
// annotation.
|
// annotation.
|
||||||
vec![],
|
std::iter::empty(),
|
||||||
vec![],
|
std::iter::empty(),
|
||||||
Constraint::True,
|
Constraint::True,
|
||||||
constraint,
|
constraint,
|
||||||
def_pattern_state,
|
def_pattern_state,
|
||||||
|
@ -170,39 +169,44 @@ fn constrain_symbols_from_requires(
|
||||||
pub fn frontload_ability_constraints(
|
pub fn frontload_ability_constraints(
|
||||||
constraints: &mut Constraints,
|
constraints: &mut Constraints,
|
||||||
abilities_store: &AbilitiesStore,
|
abilities_store: &AbilitiesStore,
|
||||||
|
home: ModuleId,
|
||||||
mut constraint: Constraint,
|
mut constraint: Constraint,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
for (member_name, member_data) in abilities_store.root_ability_members().iter() {
|
for (member_name, member_data) in abilities_store.root_ability_members().iter() {
|
||||||
// 1. Attach the type of member signature to the reserved signature_var. This is
|
let rigids = Default::default();
|
||||||
// infallible.
|
let env = Env { home, rigids };
|
||||||
let unify_with_signature_var = constraints.equal_types_var(
|
let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(*member_name));
|
||||||
|
|
||||||
|
let mut def_pattern_state = constrain_def_pattern(
|
||||||
|
constraints,
|
||||||
|
&env,
|
||||||
|
&pattern,
|
||||||
|
Type::Variable(member_data.signature_var),
|
||||||
|
);
|
||||||
|
|
||||||
|
def_pattern_state.vars.push(member_data.signature_var);
|
||||||
|
|
||||||
|
let vars = &member_data.variables;
|
||||||
|
let rigid_variables = vars.rigid_vars.iter().chain(vars.able_vars.iter()).copied();
|
||||||
|
let infer_variables = vars.flex_vars.iter().copied();
|
||||||
|
|
||||||
|
def_pattern_state
|
||||||
|
.constraints
|
||||||
|
.push(constraints.equal_types_var(
|
||||||
member_data.signature_var,
|
member_data.signature_var,
|
||||||
Expected::NoExpectation(member_data.signature.clone()),
|
Expected::NoExpectation(member_data.signature.clone()),
|
||||||
Category::Storage(std::file!(), std::column!()),
|
Category::Storage(file!(), line!()),
|
||||||
Region::zero(),
|
Region::zero(),
|
||||||
);
|
));
|
||||||
|
|
||||||
// 2. Store the member signature on the member symbol. This makes sure we generalize it on
|
constraint = constrain_def_make_constraint(
|
||||||
// the toplevel, as appropriate.
|
constraints,
|
||||||
let vars = &member_data.variables;
|
rigid_variables,
|
||||||
let rigids = (vars.rigid_vars.iter())
|
infer_variables,
|
||||||
// For our purposes, in the let constraint, able vars are treated like rigids.
|
|
||||||
.chain(vars.able_vars.iter())
|
|
||||||
.copied();
|
|
||||||
let flex = vars.flex_vars.iter().copied();
|
|
||||||
|
|
||||||
let let_constr = constraints.let_constraint(
|
|
||||||
rigids,
|
|
||||||
flex,
|
|
||||||
[(
|
|
||||||
*member_name,
|
|
||||||
Loc::at_zero(Type::Variable(member_data.signature_var)),
|
|
||||||
)],
|
|
||||||
Constraint::True,
|
Constraint::True,
|
||||||
constraint,
|
constraint,
|
||||||
|
def_pattern_state,
|
||||||
);
|
);
|
||||||
|
|
||||||
constraint = constraints.and_constraint([unify_with_signature_var, let_constr]);
|
|
||||||
}
|
}
|
||||||
constraint
|
constraint
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
AliasKind, Category, PReason, PatternCategory, Reason, RecordField, Type, TypeExtension,
|
AliasKind, Category, OptAbleType, PReason, PatternCategory, Reason, RecordField, Type,
|
||||||
|
TypeExtension,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -514,7 +515,13 @@ pub fn constrain_pattern(
|
||||||
|
|
||||||
let opaque_type = Type::Alias {
|
let opaque_type = Type::Alias {
|
||||||
symbol: *opaque,
|
symbol: *opaque,
|
||||||
type_arguments: type_arguments.iter().copied().map(Type::Variable).collect(),
|
type_arguments: type_arguments
|
||||||
|
.iter()
|
||||||
|
.map(|v| OptAbleType {
|
||||||
|
typ: Type::Variable(v.var),
|
||||||
|
opt_ability: v.opt_ability,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
lambda_set_variables: lambda_set_variables.clone(),
|
lambda_set_variables: lambda_set_variables.clone(),
|
||||||
actual: Box::new(arg_pattern_type.clone()),
|
actual: Box::new(arg_pattern_type.clone()),
|
||||||
kind: AliasKind::Opaque,
|
kind: AliasKind::Opaque,
|
||||||
|
@ -571,7 +578,7 @@ pub fn constrain_pattern(
|
||||||
.vars
|
.vars
|
||||||
.extend_from_slice(&[*arg_pattern_var, *whole_var]);
|
.extend_from_slice(&[*arg_pattern_var, *whole_var]);
|
||||||
// Also add the fresh variables we created for the type argument and lambda sets
|
// Also add the fresh variables we created for the type argument and lambda sets
|
||||||
state.vars.extend(type_arguments);
|
state.vars.extend(type_arguments.iter().map(|v| v.var));
|
||||||
state.vars.extend(lambda_set_variables.iter().map(|v| {
|
state.vars.extend(lambda_set_variables.iter().map(|v| {
|
||||||
v.0.expect_variable("all lambda sets should be fresh variables here")
|
v.0.expect_variable("all lambda sets should be fresh variables here")
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -4107,7 +4107,7 @@ fn make_specializations<'a>(
|
||||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||||
mut module_timing: ModuleTiming,
|
mut module_timing: ModuleTiming,
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
abilities_store: AbilitiesStore,
|
mut abilities_store: AbilitiesStore,
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
let make_specializations_start = SystemTime::now();
|
let make_specializations_start = SystemTime::now();
|
||||||
let mut mono_problems = Vec::new();
|
let mut mono_problems = Vec::new();
|
||||||
|
@ -4123,7 +4123,7 @@ fn make_specializations<'a>(
|
||||||
update_mode_ids: &mut update_mode_ids,
|
update_mode_ids: &mut update_mode_ids,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
abilities_store: &abilities_store,
|
abilities_store: &mut abilities_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut procs = Procs::new_in(arena);
|
let mut procs = Procs::new_in(arena);
|
||||||
|
@ -4194,7 +4194,7 @@ fn build_pending_specializations<'a>(
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
// TODO remove
|
// TODO remove
|
||||||
exposed_to_host: ExposedToHost,
|
exposed_to_host: ExposedToHost,
|
||||||
abilities_store: AbilitiesStore,
|
mut abilities_store: AbilitiesStore,
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
let find_specializations_start = SystemTime::now();
|
let find_specializations_start = SystemTime::now();
|
||||||
|
|
||||||
|
@ -4221,7 +4221,7 @@ fn build_pending_specializations<'a>(
|
||||||
update_mode_ids: &mut update_mode_ids,
|
update_mode_ids: &mut update_mode_ids,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
abilities_store: &abilities_store,
|
abilities_store: &mut abilities_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add modules' decls to Procs
|
// Add modules' decls to Procs
|
||||||
|
|
|
@ -67,6 +67,7 @@ pub fn deep_copy_type_vars_into_expr<'a>(
|
||||||
loc_elems: loc_elems.iter().map(|le| le.map(go_help)).collect(),
|
loc_elems: loc_elems.iter().map(|le| le.map(go_help)).collect(),
|
||||||
},
|
},
|
||||||
Var(sym) => Var(*sym),
|
Var(sym) => Var(*sym),
|
||||||
|
AbilityMember(sym, specialization) => AbilityMember(*sym, *specialization),
|
||||||
When {
|
When {
|
||||||
loc_cond,
|
loc_cond,
|
||||||
cond_var,
|
cond_var,
|
||||||
|
|
|
@ -7,10 +7,10 @@ use crate::layout::{
|
||||||
use bumpalo::collections::{CollectIn, Vec};
|
use bumpalo::collections::{CollectIn, Vec};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::{AbilitiesStore, SpecializationId};
|
||||||
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
|
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
|
||||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||||
use roc_collections::VecMap;
|
use roc_collections::{MutSet, VecMap};
|
||||||
use roc_debug_flags::{
|
use roc_debug_flags::{
|
||||||
dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
|
dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
|
||||||
ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
||||||
|
@ -22,6 +22,7 @@ use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_problem::can::{RuntimeError, ShadowKind};
|
use roc_problem::can::{RuntimeError, ShadowKind};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
use roc_solve::ability::resolve_ability_specialization;
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
|
@ -1253,7 +1254,7 @@ pub struct Env<'a, 'i> {
|
||||||
pub target_info: TargetInfo,
|
pub target_info: TargetInfo,
|
||||||
pub update_mode_ids: &'i mut UpdateModeIds,
|
pub update_mode_ids: &'i mut UpdateModeIds,
|
||||||
pub call_specialization_counter: u32,
|
pub call_specialization_counter: u32,
|
||||||
pub abilities_store: &'i AbilitiesStore,
|
pub abilities_store: &'i mut AbilitiesStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> Env<'a, 'i> {
|
impl<'a, 'i> Env<'a, 'i> {
|
||||||
|
@ -2479,6 +2480,106 @@ fn generate_runtime_error_function<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_abilities_in_specialized_body<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &Procs<'a>,
|
||||||
|
specialized_body: &roc_can::expr::Expr,
|
||||||
|
body_var: Variable,
|
||||||
|
) -> std::vec::Vec<SpecializationId> {
|
||||||
|
use roc_can::expr::Expr;
|
||||||
|
use roc_can::traverse::{walk_expr, PatternVisitor, Visitor};
|
||||||
|
use roc_unify::unify::unify;
|
||||||
|
|
||||||
|
struct Resolver<'a> {
|
||||||
|
subs: &'a mut Subs,
|
||||||
|
procs: &'a Procs<'a>,
|
||||||
|
abilities_store: &'a mut AbilitiesStore,
|
||||||
|
seen_defs: MutSet<Symbol>,
|
||||||
|
specialized: std::vec::Vec<SpecializationId>,
|
||||||
|
}
|
||||||
|
impl PatternVisitor for Resolver<'_> {}
|
||||||
|
impl Visitor for Resolver<'_> {
|
||||||
|
fn visit_expr(&mut self, expr: &Expr, _region: Region, var: Variable) {
|
||||||
|
match expr {
|
||||||
|
Expr::Closure(..) => {
|
||||||
|
// Don't walk down closure bodies. They will have their types refined when they
|
||||||
|
// are themselves specialized, so we'll handle ability resolution in them at
|
||||||
|
// that time too.
|
||||||
|
}
|
||||||
|
Expr::LetRec(..) | Expr::LetNonRec(..) => {
|
||||||
|
// Also don't walk down let-bindings. These may be generalized and we won't
|
||||||
|
// know their specializations until we collect them while building up the def.
|
||||||
|
// So, we'll resolve any nested abilities when we know their specialized type
|
||||||
|
// during def construction.
|
||||||
|
}
|
||||||
|
Expr::AbilityMember(member_sym, specialization_id) => {
|
||||||
|
if self
|
||||||
|
.abilities_store
|
||||||
|
.get_resolved(*specialization_id)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
// We already know the specialization from type solving; we are good to go.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let specialization = resolve_ability_specialization(
|
||||||
|
self.subs,
|
||||||
|
self.abilities_store,
|
||||||
|
*member_sym,
|
||||||
|
var,
|
||||||
|
)
|
||||||
|
.expect("Ability specialization is unknown - code generation cannot proceed!");
|
||||||
|
|
||||||
|
// We must now refine the current type state to account for this specialization,
|
||||||
|
// since `var` may only have partial specialization information - enough to
|
||||||
|
// figure out what specialization we need, but not the types of all arguments
|
||||||
|
// and return types. So, unify with the variable with the specialization's type.
|
||||||
|
let specialization_def = self
|
||||||
|
.procs
|
||||||
|
.partial_procs
|
||||||
|
.get_symbol(specialization)
|
||||||
|
.expect("Specialization found, but it's not in procs");
|
||||||
|
let specialization_var = specialization_def.annotation;
|
||||||
|
|
||||||
|
let unified = unify(self.subs, var, specialization_var, Mode::EQ);
|
||||||
|
unified.expect_success(
|
||||||
|
"Specialization does not unify - this is a typechecker bug!",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now walk the specialization def to pick up any more needed types. Of course,
|
||||||
|
// we only want to pass through it once to avoid unbounded recursion.
|
||||||
|
if !self.seen_defs.contains(&specialization) {
|
||||||
|
self.visit_expr(
|
||||||
|
&specialization_def.body,
|
||||||
|
Region::zero(),
|
||||||
|
specialization_def.body_var,
|
||||||
|
);
|
||||||
|
self.seen_defs.insert(specialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.abilities_store
|
||||||
|
.insert_resolved(*specialization_id, specialization);
|
||||||
|
|
||||||
|
debug_assert!(!self.specialized.contains(specialization_id));
|
||||||
|
self.specialized.push(*specialization_id);
|
||||||
|
}
|
||||||
|
_ => walk_expr(self, expr, var),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut specializer = Resolver {
|
||||||
|
subs: env.subs,
|
||||||
|
procs,
|
||||||
|
abilities_store: env.abilities_store,
|
||||||
|
seen_defs: MutSet::default(),
|
||||||
|
specialized: vec![],
|
||||||
|
};
|
||||||
|
specializer.visit_expr(specialized_body, Region::zero(), body_var);
|
||||||
|
|
||||||
|
specializer.specialized
|
||||||
|
}
|
||||||
|
|
||||||
fn specialize_external<'a>(
|
fn specialize_external<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
@ -2614,8 +2715,17 @@ fn specialize_external<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = partial_proc.body.clone();
|
let body = partial_proc.body.clone();
|
||||||
|
let resolved_ability_specializations =
|
||||||
|
resolve_abilities_in_specialized_body(env, procs, &body, partial_proc.body_var);
|
||||||
|
|
||||||
let mut specialized_body = from_can(env, partial_proc.body_var, body, procs, layout_cache);
|
let mut specialized_body = from_can(env, partial_proc.body_var, body, procs, layout_cache);
|
||||||
|
|
||||||
|
// reset the resolved ability specializations so as not to interfere with other specializations
|
||||||
|
// of this proc.
|
||||||
|
resolved_ability_specializations
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|sid| env.abilities_store.remove_resolved(sid));
|
||||||
|
|
||||||
match specialized {
|
match specialized {
|
||||||
SpecializedLayout::FunctionPointerBody {
|
SpecializedLayout::FunctionPointerBody {
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -3564,6 +3674,22 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol)
|
specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol)
|
||||||
}
|
}
|
||||||
|
AbilityMember(_member, specialization_id) => {
|
||||||
|
let specialization_symbol = env
|
||||||
|
.abilities_store
|
||||||
|
.get_resolved(specialization_id)
|
||||||
|
.expect("Specialization was never made!");
|
||||||
|
|
||||||
|
specialize_naked_symbol(
|
||||||
|
env,
|
||||||
|
variable,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
specialization_symbol,
|
||||||
|
)
|
||||||
|
}
|
||||||
Tag {
|
Tag {
|
||||||
variant_var,
|
variant_var,
|
||||||
name: tag_name,
|
name: tag_name,
|
||||||
|
@ -4406,14 +4532,10 @@ pub fn with_hole<'a>(
|
||||||
// a proc in this module, or an imported symbol
|
// a proc in this module, or an imported symbol
|
||||||
procs.partial_procs.contains_key(key)
|
procs.partial_procs.contains_key(key)
|
||||||
|| (env.is_imported_symbol(key) && !procs.is_imported_module_thunk(key))
|
|| (env.is_imported_symbol(key) && !procs.is_imported_module_thunk(key))
|
||||||
|| env.abilities_store.is_ability_member_name(key)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match loc_expr.value {
|
match loc_expr.value {
|
||||||
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
|
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
|
||||||
// This might be an ability member - if so, use the appropriate specialization.
|
|
||||||
let proc_name = get_specialization(env, fn_var, proc_name).unwrap_or(proc_name);
|
|
||||||
|
|
||||||
// a call by a known name
|
// a call by a known name
|
||||||
call_by_name(
|
call_by_name(
|
||||||
env,
|
env,
|
||||||
|
@ -4426,6 +4548,22 @@ pub fn with_hole<'a>(
|
||||||
hole,
|
hole,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
roc_can::expr::Expr::AbilityMember(_, specialization_id) => {
|
||||||
|
let proc_name = env.abilities_store.get_resolved(specialization_id).expect(
|
||||||
|
"Ability specialization is unknown - code generation cannot proceed!",
|
||||||
|
);
|
||||||
|
|
||||||
|
call_by_name(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
fn_var,
|
||||||
|
proc_name,
|
||||||
|
loc_args,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Call by pointer - the closure was anonymous, e.g.
|
// Call by pointer - the closure was anonymous, e.g.
|
||||||
//
|
//
|
||||||
|
@ -4543,8 +4681,8 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
UnspecializedExpr(symbol) => {
|
UnspecializedExpr(symbol) => {
|
||||||
match procs.ability_member_aliases.get(symbol).unwrap() {
|
match procs.ability_member_aliases.get(symbol).unwrap() {
|
||||||
&AbilityMember(member) => {
|
&self::AbilityMember(member) => {
|
||||||
let proc_name = get_specialization(env, fn_var, member).expect("Recorded as an ability member, but it doesn't have a specialization");
|
let proc_name = resolve_ability_specialization(env.subs, env.abilities_store, member, fn_var).expect("Recorded as an ability member, but it doesn't have a specialization");
|
||||||
|
|
||||||
// a call by a known name
|
// a call by a known name
|
||||||
return call_by_name(
|
return call_by_name(
|
||||||
|
@ -4904,45 +5042,6 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn get_specialization<'a>(
|
|
||||||
env: &mut Env<'a, '_>,
|
|
||||||
symbol_var: Variable,
|
|
||||||
symbol: Symbol,
|
|
||||||
) -> Option<Symbol> {
|
|
||||||
use roc_solve::ability::type_implementing_member;
|
|
||||||
use roc_solve::solve::instantiate_rigids;
|
|
||||||
use roc_unify::unify::unify;
|
|
||||||
|
|
||||||
match env.abilities_store.member_def(symbol) {
|
|
||||||
None => {
|
|
||||||
// This is not an ability member, it doesn't need specialization.
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Some(member) => {
|
|
||||||
let snapshot = env.subs.snapshot();
|
|
||||||
instantiate_rigids(env.subs, member.signature_var);
|
|
||||||
let (_, must_implement_ability) = unify(
|
|
||||||
env.subs,
|
|
||||||
symbol_var,
|
|
||||||
member.signature_var,
|
|
||||||
roc_unify::unify::Mode::EQ,
|
|
||||||
)
|
|
||||||
.expect_success("This typechecked previously");
|
|
||||||
env.subs.rollback_to(snapshot);
|
|
||||||
let specializing_type =
|
|
||||||
type_implementing_member(&must_implement_ability, member.parent_ability);
|
|
||||||
|
|
||||||
let specialization = env
|
|
||||||
.abilities_store
|
|
||||||
.get_specialization(symbol, specializing_type)
|
|
||||||
.expect("No specialization is recorded - I thought there would only be a type error here.");
|
|
||||||
|
|
||||||
Some(specialization.symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn construct_closure_data<'a, I>(
|
fn construct_closure_data<'a, I>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
|
@ -5707,7 +5806,8 @@ pub fn from_can<'a>(
|
||||||
|
|
||||||
return from_can(env, variable, cont.value, procs, layout_cache);
|
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
}
|
}
|
||||||
roc_can::expr::Expr::Var(original) => {
|
roc_can::expr::Expr::Var(original)
|
||||||
|
| roc_can::expr::Expr::AbilityMember(original, _) => {
|
||||||
// a variable is aliased, e.g.
|
// a variable is aliased, e.g.
|
||||||
//
|
//
|
||||||
// foo = bar
|
// foo = bar
|
||||||
|
@ -5844,6 +5944,13 @@ pub fn from_can<'a>(
|
||||||
let _res =
|
let _res =
|
||||||
roc_unify::unify::unify(env.subs, var, def.expr_var, Mode::EQ);
|
roc_unify::unify::unify(env.subs, var, def.expr_var, Mode::EQ);
|
||||||
|
|
||||||
|
resolve_abilities_in_specialized_body(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
&def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
|
);
|
||||||
|
|
||||||
return with_hole(
|
return with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
|
@ -5875,6 +5982,13 @@ pub fn from_can<'a>(
|
||||||
Mode::EQ,
|
Mode::EQ,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
resolve_abilities_in_specialized_body(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
&def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
|
);
|
||||||
|
|
||||||
stmt = with_hole(
|
stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
specialized_expr,
|
specialized_expr,
|
||||||
|
@ -5933,6 +6047,13 @@ pub fn from_can<'a>(
|
||||||
let outer_symbol = env.unique_symbol();
|
let outer_symbol = env.unique_symbol();
|
||||||
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
||||||
|
|
||||||
|
resolve_abilities_in_specialized_body(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
&def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
|
);
|
||||||
|
|
||||||
// convert the def body, store in outer_symbol
|
// convert the def body, store in outer_symbol
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
|
@ -6931,10 +7052,17 @@ fn can_reuse_symbol<'a>(
|
||||||
procs: &Procs<'a>,
|
procs: &Procs<'a>,
|
||||||
expr: &roc_can::expr::Expr,
|
expr: &roc_can::expr::Expr,
|
||||||
) -> ReuseSymbol {
|
) -> ReuseSymbol {
|
||||||
|
use roc_can::expr::Expr::*;
|
||||||
use ReuseSymbol::*;
|
use ReuseSymbol::*;
|
||||||
|
|
||||||
if let roc_can::expr::Expr::Var(symbol) = expr {
|
let symbol = match expr {
|
||||||
let symbol = *symbol;
|
AbilityMember(_, specialization_id) => env
|
||||||
|
.abilities_store
|
||||||
|
.get_resolved(*specialization_id)
|
||||||
|
.expect("Specialization must be known!"),
|
||||||
|
Var(symbol) => *symbol,
|
||||||
|
_ => return NotASymbol,
|
||||||
|
};
|
||||||
|
|
||||||
let arguments = [
|
let arguments = [
|
||||||
Symbol::ARG_1,
|
Symbol::ARG_1,
|
||||||
|
@ -6957,9 +7085,6 @@ fn can_reuse_symbol<'a>(
|
||||||
} else {
|
} else {
|
||||||
Value(symbol)
|
Value(symbol)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
NotASymbol
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn possible_reuse_symbol_or_specialize<'a>(
|
fn possible_reuse_symbol_or_specialize<'a>(
|
||||||
|
@ -6990,6 +7115,9 @@ fn handle_variable_aliasing<'a, BuildRest>(
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>,
|
BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>,
|
||||||
|
{
|
||||||
|
// 1. Handle references to ability members - we could be aliasing an ability member, or another
|
||||||
|
// alias to an ability member.
|
||||||
{
|
{
|
||||||
if env.abilities_store.is_ability_member_name(right) {
|
if env.abilities_store.is_ability_member_name(right) {
|
||||||
procs
|
procs
|
||||||
|
@ -6997,14 +7125,14 @@ where
|
||||||
.insert(left, AbilityMember(right));
|
.insert(left, AbilityMember(right));
|
||||||
return build_rest(env, procs, layout_cache);
|
return build_rest(env, procs, layout_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(&ability_member) = procs.ability_member_aliases.get(right) {
|
if let Some(&ability_member) = procs.ability_member_aliases.get(right) {
|
||||||
// If `right` links to a partial expression, make sure we link `left` to it as well, so
|
|
||||||
// that usages of it will be specialized when building the rest of the program.
|
|
||||||
procs.ability_member_aliases.insert(left, ability_member);
|
procs.ability_member_aliases.insert(left, ability_member);
|
||||||
return build_rest(env, procs, layout_cache);
|
return build_rest(env, procs, layout_cache);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Handle references to a known proc - again, we may be either aliasing the proc, or another
|
||||||
|
// alias to a proc.
|
||||||
if procs.partial_procs.contains_key(right) {
|
if procs.partial_procs.contains_key(right) {
|
||||||
// This is an alias to a function defined in this module.
|
// This is an alias to a function defined in this module.
|
||||||
// Attach the alias, then build the rest of the module, so that we reference and specialize
|
// Attach the alias, then build the rest of the module, so that we reference and specialize
|
||||||
|
@ -7041,6 +7169,8 @@ where
|
||||||
// then we must construct its closure; since imported symbols have no closure, we use the empty struct
|
// then we must construct its closure; since imported symbols have no closure, we use the empty struct
|
||||||
let_empty_struct(left, env.arena.alloc(result))
|
let_empty_struct(left, env.arena.alloc(result))
|
||||||
} else {
|
} else {
|
||||||
|
// Otherwise, we are referencing a non-proc value.
|
||||||
|
|
||||||
// We need to lift all specializations of "left" to be specializations of "right".
|
// We need to lift all specializations of "left" to be specializations of "right".
|
||||||
let mut scratchpad_update_specializations = std::vec::Vec::new();
|
let mut scratchpad_update_specializations = std::vec::Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use roc_types::types::{Category, PatternCategory};
|
||||||
use roc_unify::unify::MustImplementAbility;
|
use roc_unify::unify::MustImplementAbility;
|
||||||
use roc_unify::unify::MustImplementConstraints;
|
use roc_unify::unify::MustImplementConstraints;
|
||||||
|
|
||||||
|
use crate::solve::instantiate_rigids;
|
||||||
use crate::solve::{IncompleteAbilityImplementation, TypeError};
|
use crate::solve::{IncompleteAbilityImplementation, TypeError};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -170,12 +171,12 @@ pub fn type_implementing_member(
|
||||||
ability: Symbol,
|
ability: Symbol,
|
||||||
) -> Symbol {
|
) -> Symbol {
|
||||||
debug_assert_eq!({
|
debug_assert_eq!({
|
||||||
let ability_implementations_for_specialization =
|
|
||||||
specialization_must_implement_constraints
|
specialization_must_implement_constraints
|
||||||
.clone()
|
.clone()
|
||||||
.get_unique();
|
.get_unique()
|
||||||
|
.into_iter()
|
||||||
ability_implementations_for_specialization.len()
|
.filter(|mia| mia.ability == ability)
|
||||||
|
.count()
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",
|
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",
|
||||||
|
@ -188,3 +189,32 @@ pub fn type_implementing_member(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.typ
|
.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_ability_specialization(
|
||||||
|
subs: &mut Subs,
|
||||||
|
abilities_store: &AbilitiesStore,
|
||||||
|
ability_member: Symbol,
|
||||||
|
specialization_var: Variable,
|
||||||
|
) -> Option<Symbol> {
|
||||||
|
use roc_unify::unify::{unify, Mode};
|
||||||
|
|
||||||
|
let member_def = abilities_store
|
||||||
|
.member_def(ability_member)
|
||||||
|
.expect("Not an ability member symbol");
|
||||||
|
|
||||||
|
let snapshot = subs.snapshot();
|
||||||
|
instantiate_rigids(subs, member_def.signature_var);
|
||||||
|
let (_, must_implement_ability) =
|
||||||
|
unify(subs, specialization_var, member_def.signature_var, Mode::EQ).expect_success(
|
||||||
|
"If resolving a specialization, the specialization must be known to typecheck.",
|
||||||
|
);
|
||||||
|
|
||||||
|
subs.rollback_to(snapshot);
|
||||||
|
|
||||||
|
let specializing_type =
|
||||||
|
type_implementing_member(&must_implement_ability, member_def.parent_ability);
|
||||||
|
|
||||||
|
let specialization = abilities_store.get_specialization(ability_member, specializing_type)?;
|
||||||
|
|
||||||
|
Some(specialization.symbol)
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ use roc_types::subs::{
|
||||||
};
|
};
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, PatternCategory,
|
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, OptAbleType,
|
||||||
Reason, TypeExtension,
|
OptAbleVar, PatternCategory, Reason, TypeExtension,
|
||||||
};
|
};
|
||||||
use roc_unify::unify::{unify, Mode, Unified::*};
|
use roc_unify::unify::{unify, Mode, Unified::*};
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ struct DelayedAliasVariables {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DelayedAliasVariables {
|
impl DelayedAliasVariables {
|
||||||
fn recursion_variables(self, variables: &mut [Variable]) -> &mut [Variable] {
|
fn recursion_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] {
|
||||||
let start = self.start as usize
|
let start = self.start as usize
|
||||||
+ (self.type_variables_len + self.lambda_set_variables_len) as usize;
|
+ (self.type_variables_len + self.lambda_set_variables_len) as usize;
|
||||||
let length = self.recursion_variables_len as usize;
|
let length = self.recursion_variables_len as usize;
|
||||||
|
@ -123,14 +123,14 @@ impl DelayedAliasVariables {
|
||||||
&mut variables[start..][..length]
|
&mut variables[start..][..length]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lambda_set_variables(self, variables: &mut [Variable]) -> &mut [Variable] {
|
fn lambda_set_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] {
|
||||||
let start = self.start as usize + self.type_variables_len as usize;
|
let start = self.start as usize + self.type_variables_len as usize;
|
||||||
let length = self.lambda_set_variables_len as usize;
|
let length = self.lambda_set_variables_len as usize;
|
||||||
|
|
||||||
&mut variables[start..][..length]
|
&mut variables[start..][..length]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_variables(self, variables: &mut [Variable]) -> &mut [Variable] {
|
fn type_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] {
|
||||||
let start = self.start as usize;
|
let start = self.start as usize;
|
||||||
let length = self.type_variables_len as usize;
|
let length = self.type_variables_len as usize;
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ impl DelayedAliasVariables {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Aliases {
|
pub struct Aliases {
|
||||||
aliases: Vec<(Symbol, Type, DelayedAliasVariables, AliasKind)>,
|
aliases: Vec<(Symbol, Type, DelayedAliasVariables, AliasKind)>,
|
||||||
variables: Vec<Variable>,
|
variables: Vec<OptAbleVar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aliases {
|
impl Aliases {
|
||||||
|
@ -150,18 +150,28 @@ impl Aliases {
|
||||||
{
|
{
|
||||||
let start = self.variables.len() as _;
|
let start = self.variables.len() as _;
|
||||||
|
|
||||||
self.variables
|
self.variables.extend(
|
||||||
.extend(alias.type_variables.iter().map(|x| x.value.1));
|
alias
|
||||||
|
.type_variables
|
||||||
|
.iter()
|
||||||
|
.map(|x| OptAbleVar::from(&x.value)),
|
||||||
|
);
|
||||||
|
|
||||||
self.variables.extend(alias.lambda_set_variables.iter().map(
|
self.variables.extend(alias.lambda_set_variables.iter().map(
|
||||||
|x| match x.as_inner() {
|
|x| match x.as_inner() {
|
||||||
Type::Variable(v) => *v,
|
Type::Variable(v) => OptAbleVar::unbound(*v),
|
||||||
_ => unreachable!("lambda set type is not a variable"),
|
_ => unreachable!("lambda set type is not a variable"),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
let recursion_variables_len = alias.recursion_variables.len() as _;
|
let recursion_variables_len = alias.recursion_variables.len() as _;
|
||||||
self.variables.extend(alias.recursion_variables);
|
self.variables.extend(
|
||||||
|
alias
|
||||||
|
.recursion_variables
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(OptAbleVar::unbound),
|
||||||
|
);
|
||||||
|
|
||||||
DelayedAliasVariables {
|
DelayedAliasVariables {
|
||||||
start,
|
start,
|
||||||
|
@ -303,10 +313,14 @@ impl Aliases {
|
||||||
|
|
||||||
let mut substitutions: MutMap<_, _> = Default::default();
|
let mut substitutions: MutMap<_, _> = Default::default();
|
||||||
|
|
||||||
for rec_var in delayed_variables
|
for OptAbleVar {
|
||||||
|
var: rec_var,
|
||||||
|
opt_ability,
|
||||||
|
} in delayed_variables
|
||||||
.recursion_variables(&mut self.variables)
|
.recursion_variables(&mut self.variables)
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
{
|
{
|
||||||
|
debug_assert!(opt_ability.is_none());
|
||||||
let new_var = subs.fresh_unnamed_flex_var();
|
let new_var = subs.fresh_unnamed_flex_var();
|
||||||
substitutions.insert(*rec_var, new_var);
|
substitutions.insert(*rec_var, new_var);
|
||||||
*rec_var = new_var;
|
*rec_var = new_var;
|
||||||
|
@ -318,10 +332,10 @@ impl Aliases {
|
||||||
for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) {
|
for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) {
|
||||||
// if constraint gen duplicated a type these variables could be the same
|
// if constraint gen duplicated a type these variables could be the same
|
||||||
// (happens very often in practice)
|
// (happens very often in practice)
|
||||||
if *old != *new {
|
if old.var != *new {
|
||||||
substitutions.insert(*old, *new);
|
substitutions.insert(old.var, *new);
|
||||||
|
|
||||||
*old = *new;
|
old.var = *new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,9 +347,10 @@ impl Aliases {
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(new_lambda_set_variables)
|
.zip(new_lambda_set_variables)
|
||||||
{
|
{
|
||||||
if *old != *new {
|
debug_assert!(old.opt_ability.is_none());
|
||||||
substitutions.insert(*old, *new);
|
if old.var != *new {
|
||||||
*old = *new;
|
substitutions.insert(old.var, *new);
|
||||||
|
old.var = *new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1937,8 +1952,39 @@ fn type_to_variable<'a>(
|
||||||
let length = type_arguments.len() + lambda_set_variables.len();
|
let length = type_arguments.len() + lambda_set_variables.len();
|
||||||
let new_variables = VariableSubsSlice::reserve_into_subs(subs, length);
|
let new_variables = VariableSubsSlice::reserve_into_subs(subs, length);
|
||||||
|
|
||||||
for (target_index, arg_type) in (new_variables.indices()).zip(type_arguments) {
|
for (target_index, OptAbleType { typ, opt_ability }) in
|
||||||
let copy_var = helper!(arg_type);
|
(new_variables.indices()).zip(type_arguments)
|
||||||
|
{
|
||||||
|
let copy_var = match opt_ability {
|
||||||
|
None => helper!(typ),
|
||||||
|
Some(ability) => {
|
||||||
|
// If this type argument is marked as being bound to an ability, we must
|
||||||
|
// now correctly instantiate it as so.
|
||||||
|
match RegisterVariable::from_type(subs, rank, pools, arena, typ) {
|
||||||
|
RegisterVariable::Direct(var) => {
|
||||||
|
use Content::*;
|
||||||
|
match *subs.get_content_without_compacting(var) {
|
||||||
|
FlexVar(opt_name) => subs
|
||||||
|
.set_content(var, FlexAbleVar(opt_name, *ability)),
|
||||||
|
RigidVar(..) => internal_error!("Rigid var in type arg for {:?} - this is a bug in the solver, or our understanding", actual),
|
||||||
|
RigidAbleVar(..) | FlexAbleVar(..) => internal_error!("Able var in type arg for {:?} - this is a bug in the solver, or our understanding", actual),
|
||||||
|
_ => {
|
||||||
|
// TODO associate the type to the bound ability, and check
|
||||||
|
// that it correctly implements the ability.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var
|
||||||
|
}
|
||||||
|
RegisterVariable::Deferred => {
|
||||||
|
// TODO associate the type to the bound ability, and check
|
||||||
|
// that it correctly implements the ability.
|
||||||
|
let var = subs.fresh_unnamed_flex_var();
|
||||||
|
stack.push(TypeToVar::Defer(typ, var));
|
||||||
|
var
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
subs.variables[target_index] = copy_var;
|
subs.variables[target_index] = copy_var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ mod solve_expr {
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE_TYPE_QUERY: Regex = Regex::new(r#"^\s*#\s*(?P<where>\^+)\s*$"#).unwrap();
|
static ref RE_TYPE_QUERY: Regex =
|
||||||
|
Regex::new(r#"(?P<where>\^+)(?:\{-(?P<sub>\d+)\})?"#).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -35,9 +36,14 @@ mod solve_expr {
|
||||||
let line_info = LineInfo::new(src);
|
let line_info = LineInfo::new(src);
|
||||||
let mut queries = vec![];
|
let mut queries = vec![];
|
||||||
for (i, line) in src.lines().enumerate() {
|
for (i, line) in src.lines().enumerate() {
|
||||||
if let Some(capture) = RE_TYPE_QUERY.captures(line) {
|
for capture in RE_TYPE_QUERY.captures_iter(line) {
|
||||||
let wher = capture.name("where").unwrap();
|
let wher = capture.name("where").unwrap();
|
||||||
|
let subtract_col = capture
|
||||||
|
.name("sub")
|
||||||
|
.and_then(|m| str::parse(m.as_str()).ok())
|
||||||
|
.unwrap_or(0);
|
||||||
let (start, end) = (wher.start() as u32, wher.end() as u32);
|
let (start, end) = (wher.start() as u32, wher.end() as u32);
|
||||||
|
let (start, end) = (start - subtract_col, end - subtract_col);
|
||||||
let last_line = i as u32 - 1;
|
let last_line = i as u32 - 1;
|
||||||
let start_lc = LineColumn {
|
let start_lc = LineColumn {
|
||||||
line: last_line,
|
line: last_line,
|
||||||
|
@ -273,7 +279,8 @@ mod solve_expr {
|
||||||
let start = region.start().offset;
|
let start = region.start().offset;
|
||||||
let end = region.end().offset;
|
let end = region.end().offset;
|
||||||
let text = &src[start as usize..end as usize];
|
let text = &src[start as usize..end as usize];
|
||||||
let var = find_type_at(region, &decls).expect(&format!("No type for {}!", &text));
|
let var = find_type_at(region, &decls)
|
||||||
|
.expect(&format!("No type for {} ({:?})!", &text, region));
|
||||||
|
|
||||||
name_all_type_vars(var, subs);
|
name_all_type_vars(var, subs);
|
||||||
let content = subs.get_content_without_compacting(var);
|
let content = subs.get_content_without_compacting(var);
|
||||||
|
@ -6232,7 +6239,7 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"F b -> b",
|
"F b -> b",
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -6252,4 +6259,129 @@ mod solve_expr {
|
||||||
"MyResult",
|
"MyResult",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_propagates_able_var() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ zeroEncoder ] to "./platform"
|
||||||
|
|
||||||
|
Encoder fmt := List U8, fmt -> List U8 | fmt has Format
|
||||||
|
|
||||||
|
Format has it : fmt -> {} | fmt has Format
|
||||||
|
|
||||||
|
zeroEncoder = @Encoder \lst, _ -> lst
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Encoder a | a has Format",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encoder() {
|
||||||
|
infer_queries(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ myU8Bytes ] to "./platform"
|
||||||
|
|
||||||
|
Encoder fmt := List U8, fmt -> List U8 | fmt has Format
|
||||||
|
|
||||||
|
Encoding has
|
||||||
|
toEncoder : val -> Encoder fmt | val has Encoding, fmt has Format
|
||||||
|
|
||||||
|
Format has
|
||||||
|
u8 : U8 -> Encoder fmt | fmt has Format
|
||||||
|
|
||||||
|
appendWith : List U8, Encoder fmt, fmt -> List U8 | fmt has Format
|
||||||
|
appendWith = \lst, (@Encoder doFormat), fmt -> doFormat lst fmt
|
||||||
|
|
||||||
|
toBytes : val, fmt -> List U8 | val has Encoding, fmt has Format
|
||||||
|
toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt
|
||||||
|
|
||||||
|
|
||||||
|
Linear := {}
|
||||||
|
|
||||||
|
# impl Format for Linear
|
||||||
|
u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n)
|
||||||
|
#^^{-1}
|
||||||
|
|
||||||
|
MyU8 := U8
|
||||||
|
|
||||||
|
# impl Encoding for MyU8
|
||||||
|
toEncoder = \@MyU8 n -> u8 n
|
||||||
|
#^^^^^^^^^{-1}
|
||||||
|
|
||||||
|
myU8Bytes = toBytes (@MyU8 15) (@Linear {})
|
||||||
|
#^^^^^^^^^{-1}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"u8 : U8 -> Encoder Linear",
|
||||||
|
"toEncoder : MyU8 -> Encoder fmt | fmt has Format",
|
||||||
|
"myU8Bytes : List U8",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decoder() {
|
||||||
|
infer_queries(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ myU8 ] to "./platform"
|
||||||
|
|
||||||
|
DecodeError : [ TooShort, Leftover (List U8) ]
|
||||||
|
|
||||||
|
Decoder val fmt := List U8, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting
|
||||||
|
|
||||||
|
Decoding has
|
||||||
|
decoder : Decoder val fmt | val has Decoding, fmt has DecoderFormatting
|
||||||
|
|
||||||
|
DecoderFormatting has
|
||||||
|
u8 : Decoder U8 fmt | fmt has DecoderFormatting
|
||||||
|
|
||||||
|
decodeWith : List U8, Decoder val fmt, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting
|
||||||
|
decodeWith = \lst, (@Decoder doDecode), fmt -> doDecode lst fmt
|
||||||
|
|
||||||
|
fromBytes : List U8, fmt -> Result val DecodeError
|
||||||
|
| fmt has DecoderFormatting, val has Decoding
|
||||||
|
fromBytes = \lst, fmt ->
|
||||||
|
when decodeWith lst decoder fmt is
|
||||||
|
{ result, rest } ->
|
||||||
|
when result is
|
||||||
|
Ok val -> if List.isEmpty rest then Ok val else Err (Leftover rest)
|
||||||
|
Err e -> Err e
|
||||||
|
|
||||||
|
|
||||||
|
Linear := {}
|
||||||
|
|
||||||
|
# impl DecoderFormatting for Linear
|
||||||
|
u8 = @Decoder \lst, @Linear {} ->
|
||||||
|
#^^{-1}
|
||||||
|
when List.first lst is
|
||||||
|
Ok n -> { result: Ok n, rest: List.dropFirst lst }
|
||||||
|
Err _ -> { result: Err TooShort, rest: [] }
|
||||||
|
|
||||||
|
MyU8 := U8
|
||||||
|
|
||||||
|
# impl Decoding for MyU8
|
||||||
|
decoder = @Decoder \lst, fmt ->
|
||||||
|
#^^^^^^^{-1}
|
||||||
|
when decodeWith lst u8 fmt is
|
||||||
|
{ result, rest } ->
|
||||||
|
{ result: Result.map result (\n -> @MyU8 n), rest }
|
||||||
|
|
||||||
|
myU8 : Result MyU8 _
|
||||||
|
myU8 = fromBytes [ 15 ] (@Linear {})
|
||||||
|
#^^^^{-1}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"u8 : Decoder U8 Linear",
|
||||||
|
"decoder : Decoder MyU8 fmt | fmt has DecoderFormatting",
|
||||||
|
"myU8 : Result MyU8 DecodeError",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ use crate::helpers::wasm::assert_evals_to;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use roc_std::RocList;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn hash_specialization() {
|
fn hash_specialization() {
|
||||||
|
@ -216,3 +219,107 @@ fn ability_used_as_type_still_compiles() {
|
||||||
u64
|
u64
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
fn encode() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ myU8Bytes ] to "./platform"
|
||||||
|
|
||||||
|
Encoder fmt := List U8, fmt -> List U8 | fmt has Format
|
||||||
|
|
||||||
|
Encoding has
|
||||||
|
toEncoder : val -> Encoder fmt | val has Encoding, fmt has Format
|
||||||
|
|
||||||
|
Format has
|
||||||
|
u8 : U8 -> Encoder fmt | fmt has Format
|
||||||
|
|
||||||
|
appendWith : List U8, Encoder fmt, fmt -> List U8 | fmt has Format
|
||||||
|
appendWith = \lst, (@Encoder doFormat), fmt -> doFormat lst fmt
|
||||||
|
|
||||||
|
toBytes : val, fmt -> List U8 | val has Encoding, fmt has Format
|
||||||
|
toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt
|
||||||
|
|
||||||
|
|
||||||
|
Linear := {}
|
||||||
|
|
||||||
|
# impl Format for Linear
|
||||||
|
u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n)
|
||||||
|
|
||||||
|
Rgba := { r : U8, g : U8, b : U8, a : U8 }
|
||||||
|
|
||||||
|
# impl Encoding for Rgba
|
||||||
|
toEncoder = \@Rgba {r, g, b, a} ->
|
||||||
|
@Encoder \lst, fmt -> lst
|
||||||
|
|> appendWith (u8 r) fmt
|
||||||
|
|> appendWith (u8 g) fmt
|
||||||
|
|> appendWith (u8 b) fmt
|
||||||
|
|> appendWith (u8 a) fmt
|
||||||
|
|
||||||
|
myU8Bytes = toBytes (@Rgba { r: 106, g: 90, b: 205, a: 255 }) (@Linear {})
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[106, 90, 205, 255]),
|
||||||
|
RocList<u8>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
fn decode() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ myU8 ] to "./platform"
|
||||||
|
|
||||||
|
DecodeError : [ TooShort, Leftover (List U8) ]
|
||||||
|
|
||||||
|
Decoder val fmt := List U8, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting
|
||||||
|
|
||||||
|
Decoding has
|
||||||
|
decoder : Decoder val fmt | val has Decoding, fmt has DecoderFormatting
|
||||||
|
|
||||||
|
DecoderFormatting has
|
||||||
|
u8 : Decoder U8 fmt | fmt has DecoderFormatting
|
||||||
|
|
||||||
|
decodeWith : List U8, Decoder val fmt, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting
|
||||||
|
decodeWith = \lst, (@Decoder doDecode), fmt -> doDecode lst fmt
|
||||||
|
|
||||||
|
fromBytes : List U8, fmt -> Result val DecodeError
|
||||||
|
| fmt has DecoderFormatting, val has Decoding
|
||||||
|
fromBytes = \lst, fmt ->
|
||||||
|
when decodeWith lst decoder fmt is
|
||||||
|
{ result, rest } ->
|
||||||
|
Result.after result \val ->
|
||||||
|
if List.isEmpty rest
|
||||||
|
then Ok val
|
||||||
|
else Err (Leftover rest)
|
||||||
|
|
||||||
|
|
||||||
|
Linear := {}
|
||||||
|
|
||||||
|
# impl DecoderFormatting for Linear
|
||||||
|
u8 = @Decoder \lst, @Linear {} ->
|
||||||
|
when List.first lst is
|
||||||
|
Ok n -> { result: Ok n, rest: List.dropFirst lst }
|
||||||
|
Err _ -> { result: Err TooShort, rest: [] }
|
||||||
|
|
||||||
|
MyU8 := U8
|
||||||
|
|
||||||
|
# impl Decoding for MyU8
|
||||||
|
decoder = @Decoder \lst, fmt ->
|
||||||
|
{ result, rest } = decodeWith lst u8 fmt
|
||||||
|
{ result: Result.map result (\n -> @MyU8 n), rest }
|
||||||
|
|
||||||
|
myU8 =
|
||||||
|
when fromBytes [ 15 ] (@Linear {}) is
|
||||||
|
Ok (@MyU8 n) -> n
|
||||||
|
_ -> 27u8
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
15,
|
||||||
|
u8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1344,6 +1344,38 @@ fn opaque_assign_to_symbol() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn encode() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ myU8Bytes ] to "./platform"
|
||||||
|
|
||||||
|
Encoder fmt := List U8, fmt -> List U8 | fmt has Format
|
||||||
|
|
||||||
|
Encoding has
|
||||||
|
toEncoder : val -> Encoder fmt | val has Encoding, fmt has Format
|
||||||
|
|
||||||
|
Format has
|
||||||
|
u8 : U8 -> Encoder fmt | fmt has Format
|
||||||
|
|
||||||
|
|
||||||
|
Linear := {}
|
||||||
|
|
||||||
|
# impl Format for Linear
|
||||||
|
u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n)
|
||||||
|
|
||||||
|
MyU8 := U8
|
||||||
|
|
||||||
|
# impl Encoding for MyU8
|
||||||
|
toEncoder = \@MyU8 n -> u8 n
|
||||||
|
|
||||||
|
myU8Bytes =
|
||||||
|
when toEncoder (@MyU8 15) is
|
||||||
|
@Encoder doEncode -> doEncode [] (@Linear {})
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// #[ignore]
|
// #[ignore]
|
||||||
// #[mono_test]
|
// #[mono_test]
|
||||||
// fn static_str_closure() {
|
// fn static_str_closure() {
|
||||||
|
|
|
@ -116,7 +116,7 @@ fn find_names_needed(
|
||||||
}
|
}
|
||||||
|
|
||||||
match &subs.get_content_without_compacting(variable).clone() {
|
match &subs.get_content_without_compacting(variable).clone() {
|
||||||
RecursionVar { opt_name: None, .. } | FlexVar(None) | FlexAbleVar(None, _) => {
|
RecursionVar { opt_name: None, .. } | FlexVar(None) => {
|
||||||
let root = subs.get_root_key_without_compacting(variable);
|
let root = subs.get_root_key_without_compacting(variable);
|
||||||
|
|
||||||
// If this var is *not* its own root, then the
|
// If this var is *not* its own root, then the
|
||||||
|
@ -135,6 +135,15 @@ fn find_names_needed(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FlexAbleVar(None, _) => {
|
||||||
|
let root = subs.get_root_key_without_compacting(variable);
|
||||||
|
if !root_appearances.contains_key(&root) {
|
||||||
|
roots.push(root);
|
||||||
|
}
|
||||||
|
// Able vars are always printed at least twice (in the signature, and in the "has"
|
||||||
|
// clause set).
|
||||||
|
root_appearances.insert(root, Appearances::Multiple);
|
||||||
|
}
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
opt_name: Some(name_index),
|
opt_name: Some(name_index),
|
||||||
..
|
..
|
||||||
|
@ -271,6 +280,11 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
||||||
let content = FlexVar(Some(name_index));
|
let content = FlexVar(Some(name_index));
|
||||||
subs.set_content(root, content);
|
subs.set_content(root, content);
|
||||||
}
|
}
|
||||||
|
&FlexAbleVar(None, ability) => {
|
||||||
|
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||||
|
let content = FlexAbleVar(Some(name_index), ability);
|
||||||
|
subs.set_content(root, content);
|
||||||
|
}
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
opt_name: None,
|
opt_name: None,
|
||||||
structure,
|
structure,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::subs::{VarId, VarStore, Variable};
|
use crate::subs::{VarId, VarStore, Variable};
|
||||||
use crate::types::{AliasKind, Problem, RecordField, Type, TypeExtension};
|
use crate::types::{AliasKind, OptAbleType, Problem, RecordField, Type, TypeExtension};
|
||||||
use roc_collections::all::{ImMap, SendMap};
|
use roc_collections::all::{ImMap, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -213,7 +213,11 @@ pub fn to_type(
|
||||||
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
||||||
|
|
||||||
for solved_arg in solved_type_variables {
|
for solved_arg in solved_type_variables {
|
||||||
type_variables.push(to_type(solved_arg, free_vars, var_store));
|
type_variables.push(OptAbleType {
|
||||||
|
typ: to_type(solved_arg, free_vars, var_store),
|
||||||
|
// TODO: is this always correct?
|
||||||
|
opt_ability: None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut lambda_set_variables = Vec::with_capacity(solved_lambda_sets.len());
|
let mut lambda_set_variables = Vec::with_capacity(solved_lambda_sets.len());
|
||||||
|
|
|
@ -191,6 +191,36 @@ pub struct AliasCommon {
|
||||||
pub lambda_set_variables: Vec<LambdaSet>,
|
pub lambda_set_variables: Vec<LambdaSet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct OptAbleVar {
|
||||||
|
pub var: Variable,
|
||||||
|
pub opt_ability: Option<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptAbleVar {
|
||||||
|
pub fn unbound(var: Variable) -> Self {
|
||||||
|
Self {
|
||||||
|
var,
|
||||||
|
opt_ability: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct OptAbleType {
|
||||||
|
pub typ: Type,
|
||||||
|
pub opt_ability: Option<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptAbleType {
|
||||||
|
pub fn unbound(typ: Type) -> Self {
|
||||||
|
Self {
|
||||||
|
typ,
|
||||||
|
opt_ability: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
EmptyRec,
|
EmptyRec,
|
||||||
|
@ -208,7 +238,7 @@ pub enum Type {
|
||||||
DelayedAlias(AliasCommon),
|
DelayedAlias(AliasCommon),
|
||||||
Alias {
|
Alias {
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
type_arguments: Vec<Type>,
|
type_arguments: Vec<OptAbleType>,
|
||||||
lambda_set_variables: Vec<LambdaSet>,
|
lambda_set_variables: Vec<LambdaSet>,
|
||||||
actual: Box<Type>,
|
actual: Box<Type>,
|
||||||
kind: AliasKind,
|
kind: AliasKind,
|
||||||
|
@ -300,6 +330,16 @@ impl Clone for Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for OptAbleType {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
// This passes through `Type`, so defer to that to bump the clone counter.
|
||||||
|
Self {
|
||||||
|
typ: self.typ.clone(),
|
||||||
|
opt_ability: self.opt_ability,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub enum TypeExtension {
|
pub enum TypeExtension {
|
||||||
Open(Box<Type>),
|
Open(Box<Type>),
|
||||||
|
@ -410,7 +450,10 @@ impl fmt::Debug for Type {
|
||||||
write!(f, "(Alias {:?}", symbol)?;
|
write!(f, "(Alias {:?}", symbol)?;
|
||||||
|
|
||||||
for arg in type_arguments {
|
for arg in type_arguments {
|
||||||
write!(f, " {:?}", arg)?;
|
write!(f, " {:?}", &arg.typ)?;
|
||||||
|
if let Some(ab) = arg.opt_ability {
|
||||||
|
write!(f, ":{:?}", ab)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (lambda_set, greek_letter) in
|
for (lambda_set, greek_letter) in
|
||||||
|
@ -709,7 +752,7 @@ impl Type {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for value in type_arguments.iter_mut() {
|
for value in type_arguments.iter_mut() {
|
||||||
stack.push(value);
|
stack.push(&mut value.typ);
|
||||||
}
|
}
|
||||||
|
|
||||||
for lambda_set in lambda_set_variables.iter_mut() {
|
for lambda_set in lambda_set_variables.iter_mut() {
|
||||||
|
@ -818,7 +861,7 @@ impl Type {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for value in type_arguments.iter_mut() {
|
for value in type_arguments.iter_mut() {
|
||||||
stack.push(value);
|
stack.push(&mut value.typ);
|
||||||
}
|
}
|
||||||
for lambda_set in lambda_set_variables.iter_mut() {
|
for lambda_set in lambda_set_variables.iter_mut() {
|
||||||
stack.push(lambda_set.as_inner_mut());
|
stack.push(lambda_set.as_inner_mut());
|
||||||
|
@ -915,7 +958,7 @@ impl Type {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for ta in type_arguments {
|
for ta in type_arguments {
|
||||||
ta.substitute_alias(rep_symbol, rep_args, actual)?;
|
ta.typ.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||||
}
|
}
|
||||||
alias_actual.substitute_alias(rep_symbol, rep_args, actual)
|
alias_actual.substitute_alias(rep_symbol, rep_args, actual)
|
||||||
}
|
}
|
||||||
|
@ -1140,15 +1183,35 @@ impl Type {
|
||||||
lambda_set_variables,
|
lambda_set_variables,
|
||||||
actual: actual_type,
|
actual: actual_type,
|
||||||
..
|
..
|
||||||
|
} => {
|
||||||
|
for arg in type_args {
|
||||||
|
arg.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
||||||
}
|
}
|
||||||
| Alias {
|
|
||||||
|
for arg in lambda_set_variables {
|
||||||
|
arg.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_type.instantiate_aliases(
|
||||||
|
region,
|
||||||
|
aliases,
|
||||||
|
var_store,
|
||||||
|
new_lambda_set_variables,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Alias {
|
||||||
type_arguments: type_args,
|
type_arguments: type_args,
|
||||||
lambda_set_variables,
|
lambda_set_variables,
|
||||||
actual: actual_type,
|
actual: actual_type,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for arg in type_args {
|
for arg in type_args {
|
||||||
arg.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
arg.typ.instantiate_aliases(
|
||||||
|
region,
|
||||||
|
aliases,
|
||||||
|
var_store,
|
||||||
|
new_lambda_set_variables,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in lambda_set_variables {
|
for arg in lambda_set_variables {
|
||||||
|
@ -1210,7 +1273,12 @@ impl Type {
|
||||||
// TODO substitute further in args
|
// TODO substitute further in args
|
||||||
for (
|
for (
|
||||||
Loc {
|
Loc {
|
||||||
value: (_, placeholder),
|
value:
|
||||||
|
AliasVar {
|
||||||
|
var: placeholder,
|
||||||
|
opt_bound_ability,
|
||||||
|
..
|
||||||
|
},
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
filler,
|
filler,
|
||||||
|
@ -1223,7 +1291,10 @@ impl Type {
|
||||||
var_store,
|
var_store,
|
||||||
new_lambda_set_variables,
|
new_lambda_set_variables,
|
||||||
);
|
);
|
||||||
named_args.push(filler.clone());
|
named_args.push(OptAbleType {
|
||||||
|
typ: filler.clone(),
|
||||||
|
opt_ability: *opt_bound_ability,
|
||||||
|
});
|
||||||
substitution.insert(*placeholder, filler);
|
substitution.insert(*placeholder, filler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1509,7 +1580,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for arg in type_arguments {
|
for arg in type_arguments {
|
||||||
variables_help(arg, accum);
|
variables_help(&arg.typ, accum);
|
||||||
}
|
}
|
||||||
variables_help(actual, accum);
|
variables_help(actual, accum);
|
||||||
}
|
}
|
||||||
|
@ -1645,7 +1716,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
for arg in type_arguments {
|
for arg in type_arguments {
|
||||||
variables_help_detailed(arg, accum);
|
variables_help_detailed(&arg.typ, accum);
|
||||||
}
|
}
|
||||||
variables_help_detailed(actual, accum);
|
variables_help_detailed(actual, accum);
|
||||||
}
|
}
|
||||||
|
@ -1862,10 +1933,37 @@ pub enum AliasKind {
|
||||||
Opaque,
|
Opaque,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct AliasVar {
|
||||||
|
pub name: Lowercase,
|
||||||
|
pub var: Variable,
|
||||||
|
/// `Some` if this variable is bound to an ability; `None` otherwise.
|
||||||
|
pub opt_bound_ability: Option<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AliasVar {
|
||||||
|
pub fn unbound(name: Lowercase, var: Variable) -> AliasVar {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
var,
|
||||||
|
opt_bound_ability: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&AliasVar> for OptAbleVar {
|
||||||
|
fn from(av: &AliasVar) -> OptAbleVar {
|
||||||
|
OptAbleVar {
|
||||||
|
var: av.var,
|
||||||
|
opt_ability: av.opt_bound_ability,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Alias {
|
pub struct Alias {
|
||||||
pub region: Region,
|
pub region: Region,
|
||||||
pub type_variables: Vec<Loc<(Lowercase, Variable)>>,
|
pub type_variables: Vec<Loc<AliasVar>>,
|
||||||
|
|
||||||
/// lambda set variables, e.g. the one annotating the arrow in
|
/// lambda set variables, e.g. the one annotating the arrow in
|
||||||
/// a |c|-> b
|
/// a |c|-> b
|
||||||
|
|
|
@ -1813,11 +1813,12 @@ fn unify_flex(
|
||||||
other: &Content,
|
other: &Content,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
match other {
|
match other {
|
||||||
FlexVar(None) => {
|
FlexVar(other_opt_name) => {
|
||||||
// If both are flex, and only left has a name, keep the name around.
|
// Prefer using right's name.
|
||||||
|
let opt_name = opt_name.or(*other_opt_name);
|
||||||
match opt_able_bound {
|
match opt_able_bound {
|
||||||
Some(ability) => merge(subs, ctx, FlexAbleVar(*opt_name, ability)),
|
Some(ability) => merge(subs, ctx, FlexAbleVar(opt_name, ability)),
|
||||||
None => merge(subs, ctx, FlexVar(*opt_name)),
|
None => merge(subs, ctx, FlexVar(opt_name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1849,8 +1850,7 @@ fn unify_flex(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlexVar(Some(_))
|
RigidVar(_)
|
||||||
| RigidVar(_)
|
|
||||||
| RigidAbleVar(_, _)
|
| RigidAbleVar(_, _)
|
||||||
| RecursionVar { .. }
|
| RecursionVar { .. }
|
||||||
| Structure(_)
|
| Structure(_)
|
||||||
|
@ -1858,7 +1858,6 @@ fn unify_flex(
|
||||||
| RangedNumber(..) => {
|
| RangedNumber(..) => {
|
||||||
// TODO special-case boolean here
|
// TODO special-case boolean here
|
||||||
// In all other cases, if left is flex, defer to right.
|
// In all other cases, if left is flex, defer to right.
|
||||||
// (This includes using right's name if both are flex and named.)
|
|
||||||
merge(subs, ctx, *other)
|
merge(subs, ctx, *other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ use roc_problem::can::Problem;
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::Loc;
|
||||||
use roc_solve::solve::{self, Aliases};
|
use roc_solve::solve::{self, Aliases};
|
||||||
use roc_types::subs::{Content, Subs, VarStore, Variable};
|
use roc_types::subs::{Content, Subs, VarStore, Variable};
|
||||||
use roc_types::types::Type;
|
use roc_types::types::{AliasVar, Type};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
@ -228,7 +228,10 @@ fn add_aliases(scope: &mut Scope, var_store: &mut VarStore) {
|
||||||
let mut type_variables: Vec<_> = free_vars.unnamed_vars.into_iter().collect();
|
let mut type_variables: Vec<_> = free_vars.unnamed_vars.into_iter().collect();
|
||||||
type_variables.sort();
|
type_variables.sort();
|
||||||
for (loc_name, (_, var)) in vars.iter().zip(type_variables) {
|
for (loc_name, (_, var)) in vars.iter().zip(type_variables) {
|
||||||
variables.push(Loc::at(loc_name.region, (loc_name.value.clone(), var)));
|
variables.push(Loc::at(
|
||||||
|
loc_name.region,
|
||||||
|
AliasVar::unbound(loc_name.value.clone(), var),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.add_alias(symbol, region, variables, typ, kind);
|
scope.add_alias(symbol, region, variables, typ, kind);
|
||||||
|
|
|
@ -9019,41 +9019,6 @@ I need all branches in an `if` to have the same type!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn alias_using_ability() {
|
|
||||||
new_report_problem_as(
|
|
||||||
"alias_using_ability",
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
app "test" provides [ a ] to "./platform"
|
|
||||||
|
|
||||||
Ability has ab : a -> {} | a has Ability
|
|
||||||
|
|
||||||
Alias : Ability
|
|
||||||
|
|
||||||
a : Alias
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
── ALIAS USES ABILITY ──────────────────────────────────── /code/proj/Main.roc ─
|
|
||||||
|
|
||||||
The definition of the `Alias` aliases references the ability `Ability`:
|
|
||||||
|
|
||||||
5│ Alias : Ability
|
|
||||||
^^^^^
|
|
||||||
|
|
||||||
Abilities are not types, but you can add an ability constraint to a
|
|
||||||
type variable `a` by writing
|
|
||||||
|
|
||||||
| a has Ability
|
|
||||||
|
|
||||||
at the end of the type.
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ability_shadows_ability() {
|
fn ability_shadows_ability() {
|
||||||
new_report_problem_as(
|
new_report_problem_as(
|
||||||
|
@ -9130,37 +9095,6 @@ I need all branches in an `if` to have the same type!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ability_member_binds_extra_ability() {
|
|
||||||
new_report_problem_as(
|
|
||||||
"ability_member_binds_extra_ability",
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
app "test" provides [ eq ] to "./platform"
|
|
||||||
|
|
||||||
Eq has eq : a, a -> Bool.Bool | a has Eq
|
|
||||||
Hash has hash : a, b -> Num.U64 | a has Eq, b has Hash
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
── ABILITY MEMBER HAS EXTRANEOUS HAS CLAUSE ────────────── /code/proj/Main.roc ─
|
|
||||||
|
|
||||||
The definition of the ability member `hash` includes a has clause
|
|
||||||
binding an ability it is not a part of:
|
|
||||||
|
|
||||||
4│ Hash has hash : a, b -> Num.U64 | a has Eq, b has Hash
|
|
||||||
^^^^^^^^
|
|
||||||
|
|
||||||
Currently, ability members can only bind variables to the ability they
|
|
||||||
are a part of.
|
|
||||||
|
|
||||||
Hint: Did you mean to bind the `Hash` ability instead?
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ability_member_binds_parent_twice() {
|
fn ability_member_binds_parent_twice() {
|
||||||
new_report_problem_as(
|
new_report_problem_as(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue