mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge pull request #2838 from rtfeldman/abilities-typechecking
Inference and checking for abilities
This commit is contained in:
commit
4ea4aa4708
46 changed files with 2432 additions and 530 deletions
|
@ -33,4 +33,3 @@ tempfile = "3.2.0"
|
|||
pretty_assertions = "1.0.0"
|
||||
maplit = "1.0.2"
|
||||
indoc = "1.0.3"
|
||||
strip-ansi-escapes = "0.1.1"
|
||||
|
|
|
@ -5,6 +5,7 @@ use crossbeam::deque::{Injector, Stealer, Worker};
|
|||
use crossbeam::thread;
|
||||
use parking_lot::Mutex;
|
||||
use roc_builtins::std::borrow_stdlib;
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||
use roc_can::def::Declaration;
|
||||
use roc_can::module::{canonicalize_module_defs, Module};
|
||||
|
@ -31,6 +32,7 @@ use roc_parse::ident::UppercaseIdent;
|
|||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{FileError, Parser, SyntaxError};
|
||||
use roc_region::all::{LineInfo, Loc, Region};
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_solve::module::SolvedModule;
|
||||
use roc_solve::solve;
|
||||
use roc_target::TargetInfo;
|
||||
|
@ -347,6 +349,7 @@ pub struct LoadedModule {
|
|||
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||
pub abilities_store: AbilitiesStore,
|
||||
}
|
||||
|
||||
impl LoadedModule {
|
||||
|
@ -508,6 +511,7 @@ enum Msg<'a> {
|
|||
decls: Vec<Declaration>,
|
||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||
module_timing: ModuleTiming,
|
||||
abilities_store: AbilitiesStore,
|
||||
},
|
||||
FinishedAllTypeChecking {
|
||||
solved_subs: Solved<Subs>,
|
||||
|
@ -515,6 +519,7 @@ enum Msg<'a> {
|
|||
exposed_aliases_by_symbol: MutMap<Symbol, (bool, Alias)>,
|
||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||
abilities_store: AbilitiesStore,
|
||||
},
|
||||
FoundSpecializations {
|
||||
module_id: ModuleId,
|
||||
|
@ -604,6 +609,8 @@ struct State<'a> {
|
|||
// (Granted, this has not been attempted or measured!)
|
||||
pub layout_caches: std::vec::Vec<LayoutCache<'a>>,
|
||||
|
||||
pub render: RenderTarget,
|
||||
|
||||
// cached subs (used for builtin modules, could include packages in the future too)
|
||||
cached_subs: CachedSubs,
|
||||
}
|
||||
|
@ -611,6 +618,7 @@ struct State<'a> {
|
|||
type CachedSubs = Arc<Mutex<MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>>>;
|
||||
|
||||
impl<'a> State<'a> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
root_id: ModuleId,
|
||||
target_info: TargetInfo,
|
||||
|
@ -619,6 +627,7 @@ impl<'a> State<'a> {
|
|||
arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
||||
render: RenderTarget,
|
||||
) -> Self {
|
||||
let arc_shorthands = Arc::new(Mutex::new(MutMap::default()));
|
||||
|
||||
|
@ -643,6 +652,7 @@ impl<'a> State<'a> {
|
|||
timings: MutMap::default(),
|
||||
layout_caches: std::vec::Vec::with_capacity(num_cpus::get()),
|
||||
cached_subs: Arc::new(Mutex::new(cached_subs)),
|
||||
render,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -824,6 +834,7 @@ pub fn load_and_typecheck_str<'a>(
|
|||
src_dir: &Path,
|
||||
exposed_types: ExposedByModule,
|
||||
target_info: TargetInfo,
|
||||
render: RenderTarget,
|
||||
) -> Result<LoadedModule, LoadingProblem<'a>> {
|
||||
use LoadResult::*;
|
||||
|
||||
|
@ -841,12 +852,19 @@ pub fn load_and_typecheck_str<'a>(
|
|||
Phase::SolveTypes,
|
||||
target_info,
|
||||
cached_subs,
|
||||
render,
|
||||
)? {
|
||||
Monomorphized(_) => unreachable!(""),
|
||||
TypeChecked(module) => Ok(module),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PrintTarget {
|
||||
ColorTerminal,
|
||||
Generic,
|
||||
}
|
||||
|
||||
pub struct LoadStart<'a> {
|
||||
arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
|
@ -855,7 +873,11 @@ pub struct LoadStart<'a> {
|
|||
}
|
||||
|
||||
impl<'a> LoadStart<'a> {
|
||||
pub fn from_path(arena: &'a Bump, filename: PathBuf) -> Result<Self, LoadingProblem<'a>> {
|
||||
pub fn from_path(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
render: RenderTarget,
|
||||
) -> Result<Self, LoadingProblem<'a>> {
|
||||
let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
|
||||
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
|
||||
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
|
||||
|
@ -887,7 +909,12 @@ impl<'a> LoadStart<'a> {
|
|||
|
||||
// if parsing failed, this module did not add any identifiers
|
||||
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
|
||||
let buf = to_parse_problem_report(problem, module_ids, root_exposed_ident_ids);
|
||||
let buf = to_parse_problem_report(
|
||||
problem,
|
||||
module_ids,
|
||||
root_exposed_ident_ids,
|
||||
render,
|
||||
);
|
||||
return Err(LoadingProblem::FormattedReport(buf));
|
||||
}
|
||||
Err(LoadingProblem::FileProblem { filename, error }) => {
|
||||
|
@ -995,6 +1022,7 @@ pub fn load<'a>(
|
|||
goal_phase: Phase,
|
||||
target_info: TargetInfo,
|
||||
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
||||
render: RenderTarget,
|
||||
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
||||
// When compiling to wasm, we cannot spawn extra threads
|
||||
// so we have a single-threaded implementation
|
||||
|
@ -1007,6 +1035,7 @@ pub fn load<'a>(
|
|||
goal_phase,
|
||||
target_info,
|
||||
cached_subs,
|
||||
render,
|
||||
)
|
||||
} else {
|
||||
load_multi_threaded(
|
||||
|
@ -1017,6 +1046,7 @@ pub fn load<'a>(
|
|||
goal_phase,
|
||||
target_info,
|
||||
cached_subs,
|
||||
render,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1031,12 +1061,14 @@ fn load_single_threaded<'a>(
|
|||
goal_phase: Phase,
|
||||
target_info: TargetInfo,
|
||||
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
||||
render: RenderTarget,
|
||||
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
||||
let LoadStart {
|
||||
arc_modules,
|
||||
ident_ids_by_module,
|
||||
root_id,
|
||||
root_msg,
|
||||
..
|
||||
} = load_start;
|
||||
|
||||
let (msg_tx, msg_rx) = bounded(1024);
|
||||
|
@ -1053,6 +1085,7 @@ fn load_single_threaded<'a>(
|
|||
arc_modules,
|
||||
ident_ids_by_module,
|
||||
cached_subs,
|
||||
render,
|
||||
);
|
||||
|
||||
// We'll add tasks to this, and then worker threads will take tasks from it.
|
||||
|
@ -1115,6 +1148,7 @@ fn state_thread_step<'a>(
|
|||
exposed_aliases_by_symbol,
|
||||
dep_idents,
|
||||
documentation,
|
||||
abilities_store,
|
||||
} => {
|
||||
// We're done! There should be no more messages pending.
|
||||
debug_assert!(msg_rx.is_empty());
|
||||
|
@ -1131,6 +1165,7 @@ fn state_thread_step<'a>(
|
|||
exposed_vars_by_symbol,
|
||||
dep_idents,
|
||||
documentation,
|
||||
abilities_store,
|
||||
);
|
||||
|
||||
Ok(ControlFlow::Break(LoadResult::TypeChecked(typechecked)))
|
||||
|
@ -1153,8 +1188,12 @@ fn state_thread_step<'a>(
|
|||
|
||||
Msg::FailedToParse(problem) => {
|
||||
let module_ids = (*state.arc_modules).lock().clone().into_module_ids();
|
||||
let buf =
|
||||
to_parse_problem_report(problem, module_ids, state.constrained_ident_ids);
|
||||
let buf = to_parse_problem_report(
|
||||
problem,
|
||||
module_ids,
|
||||
state.constrained_ident_ids,
|
||||
state.render,
|
||||
);
|
||||
Err(LoadingProblem::FormattedReport(buf))
|
||||
}
|
||||
msg => {
|
||||
|
@ -1164,6 +1203,8 @@ fn state_thread_step<'a>(
|
|||
let constrained_ident_ids = state.constrained_ident_ids.clone();
|
||||
let arc_modules = state.arc_modules.clone();
|
||||
|
||||
let render = state.render;
|
||||
|
||||
let res_state = update(
|
||||
state,
|
||||
msg,
|
||||
|
@ -1185,8 +1226,12 @@ fn state_thread_step<'a>(
|
|||
.into_inner()
|
||||
.into_module_ids();
|
||||
|
||||
let buf =
|
||||
to_parse_problem_report(problem, module_ids, constrained_ident_ids);
|
||||
let buf = to_parse_problem_report(
|
||||
problem,
|
||||
module_ids,
|
||||
constrained_ident_ids,
|
||||
render,
|
||||
);
|
||||
Err(LoadingProblem::FormattedReport(buf))
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
|
@ -1210,12 +1255,14 @@ fn load_multi_threaded<'a>(
|
|||
goal_phase: Phase,
|
||||
target_info: TargetInfo,
|
||||
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
||||
render: RenderTarget,
|
||||
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
||||
let LoadStart {
|
||||
arc_modules,
|
||||
ident_ids_by_module,
|
||||
root_id,
|
||||
root_msg,
|
||||
..
|
||||
} = load_start;
|
||||
|
||||
let mut state = State::new(
|
||||
|
@ -1226,6 +1273,7 @@ fn load_multi_threaded<'a>(
|
|||
arc_modules,
|
||||
ident_ids_by_module,
|
||||
cached_subs,
|
||||
render,
|
||||
);
|
||||
|
||||
let (msg_tx, msg_rx) = bounded(1024);
|
||||
|
@ -1746,6 +1794,7 @@ fn update<'a>(
|
|||
decls,
|
||||
dep_idents,
|
||||
mut module_timing,
|
||||
abilities_store,
|
||||
} => {
|
||||
log!("solved types for {:?}", module_id);
|
||||
module_timing.end_time = SystemTime::now();
|
||||
|
@ -1798,6 +1847,7 @@ fn update<'a>(
|
|||
exposed_aliases_by_symbol: solved_module.aliases,
|
||||
dep_idents,
|
||||
documentation,
|
||||
abilities_store,
|
||||
})
|
||||
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
||||
|
||||
|
@ -2126,6 +2176,7 @@ fn finish(
|
|||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||
abilities_store: AbilitiesStore,
|
||||
) -> LoadedModule {
|
||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
||||
|
@ -2160,6 +2211,7 @@ fn finish(
|
|||
sources,
|
||||
timings: state.timings,
|
||||
documentation,
|
||||
abilities_store,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3102,6 +3154,10 @@ fn add_imports(
|
|||
rigid_vars.extend(copied_import.rigid);
|
||||
rigid_vars.extend(copied_import.flex);
|
||||
|
||||
// Rigid vars bound to abilities are also treated like rigids.
|
||||
rigid_vars.extend(copied_import.rigid_able);
|
||||
rigid_vars.extend(copied_import.flex_able);
|
||||
|
||||
import_variables.extend(copied_import.registered);
|
||||
|
||||
def_types.push((
|
||||
|
@ -3119,6 +3175,7 @@ fn add_imports(
|
|||
import_variables
|
||||
}
|
||||
|
||||
#[allow(clippy::complexity)]
|
||||
fn run_solve_solve(
|
||||
imported_builtins: Vec<Symbol>,
|
||||
exposed_for_module: ExposedForModule,
|
||||
|
@ -3126,11 +3183,17 @@ fn run_solve_solve(
|
|||
constraint: ConstraintSoa,
|
||||
mut var_store: VarStore,
|
||||
module: Module,
|
||||
) -> (Solved<Subs>, Vec<(Symbol, Variable)>, Vec<solve::TypeError>) {
|
||||
) -> (
|
||||
Solved<Subs>,
|
||||
Vec<(Symbol, Variable)>,
|
||||
Vec<solve::TypeError>,
|
||||
AbilitiesStore,
|
||||
) {
|
||||
let Module {
|
||||
exposed_symbols,
|
||||
aliases,
|
||||
rigid_variables,
|
||||
abilities_store,
|
||||
..
|
||||
} = module;
|
||||
|
||||
|
@ -3155,12 +3218,13 @@ fn run_solve_solve(
|
|||
solve_aliases.insert(*name, alias.clone());
|
||||
}
|
||||
|
||||
let (solved_subs, solved_env, problems) = roc_solve::module::run_solve(
|
||||
let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve(
|
||||
&constraints,
|
||||
actual_constraint,
|
||||
rigid_variables,
|
||||
subs,
|
||||
solve_aliases,
|
||||
abilities_store,
|
||||
);
|
||||
|
||||
let solved_subs = if true {
|
||||
|
@ -3179,7 +3243,12 @@ fn run_solve_solve(
|
|||
.filter(|(k, _)| exposed_symbols.contains(k))
|
||||
.collect();
|
||||
|
||||
(solved_subs, exposed_vars_by_symbol, problems)
|
||||
(
|
||||
solved_subs,
|
||||
exposed_vars_by_symbol,
|
||||
problems,
|
||||
abilities_store,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -3203,7 +3272,7 @@ fn run_solve<'a>(
|
|||
// TODO remove when we write builtins in roc
|
||||
let aliases = module.aliases.clone();
|
||||
|
||||
let (solved_subs, exposed_vars_by_symbol, problems) = {
|
||||
let (solved_subs, exposed_vars_by_symbol, problems, abilities_store) = {
|
||||
if module_id.is_builtin() {
|
||||
match cached_subs.lock().remove(&module_id) {
|
||||
None => {
|
||||
|
@ -3217,9 +3286,13 @@ fn run_solve<'a>(
|
|||
module,
|
||||
)
|
||||
}
|
||||
Some((subs, exposed_vars_by_symbol)) => {
|
||||
(Solved(subs), exposed_vars_by_symbol.to_vec(), vec![])
|
||||
}
|
||||
Some((subs, exposed_vars_by_symbol)) => (
|
||||
Solved(subs),
|
||||
exposed_vars_by_symbol.to_vec(),
|
||||
vec![],
|
||||
// TODO(abilities) replace when we have abilities for builtins
|
||||
AbilitiesStore::default(),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
run_solve_solve(
|
||||
|
@ -3258,6 +3331,7 @@ fn run_solve<'a>(
|
|||
dep_idents,
|
||||
solved_module,
|
||||
module_timing,
|
||||
abilities_store,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3385,8 +3459,12 @@ fn canonicalize_and_constrain<'a>(
|
|||
|
||||
let mut constraints = Constraints::new();
|
||||
|
||||
let constraint =
|
||||
constrain_module(&mut constraints, &module_output.declarations, module_id);
|
||||
let constraint = constrain_module(
|
||||
&mut constraints,
|
||||
&module_output.scope.abilities_store,
|
||||
&module_output.declarations,
|
||||
module_id,
|
||||
);
|
||||
|
||||
let after = roc_types::types::get_type_clone_count();
|
||||
|
||||
|
@ -3426,6 +3504,7 @@ fn canonicalize_and_constrain<'a>(
|
|||
referenced_types: module_output.referenced_types,
|
||||
aliases,
|
||||
rigid_variables: module_output.rigid_variables,
|
||||
abilities_store: module_output.scope.abilities_store,
|
||||
};
|
||||
|
||||
let constrained_module = ConstrainedModule {
|
||||
|
@ -4069,6 +4148,7 @@ fn to_parse_problem_report<'a>(
|
|||
problem: FileError<'a, SyntaxError<'a>>,
|
||||
mut module_ids: ModuleIds,
|
||||
all_ident_ids: MutMap<ModuleId, IdentIds>,
|
||||
render: RenderTarget,
|
||||
) -> String {
|
||||
use roc_reporting::report::{parse_problem, RocDocAllocator, DEFAULT_PALETTE};
|
||||
|
||||
|
@ -4103,7 +4183,7 @@ fn to_parse_problem_report<'a>(
|
|||
let mut buf = String::new();
|
||||
let palette = DEFAULT_PALETTE;
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
report.render(render, &mut buf, &alloc, &palette);
|
||||
|
||||
buf
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ mod test_load {
|
|||
use roc_problem::can::Problem;
|
||||
use roc_region::all::LineInfo;
|
||||
use roc_reporting::report::can_problem;
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_reporting::report::RocDocAllocator;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||
|
@ -41,7 +42,7 @@ mod test_load {
|
|||
) -> Result<LoadedModule, LoadingProblem<'a>> {
|
||||
use LoadResult::*;
|
||||
|
||||
let load_start = LoadStart::from_path(arena, filename)?;
|
||||
let load_start = LoadStart::from_path(arena, filename, RenderTarget::Generic)?;
|
||||
|
||||
match roc_load_internal::file::load(
|
||||
arena,
|
||||
|
@ -51,6 +52,7 @@ mod test_load {
|
|||
Phase::SolveTypes,
|
||||
target_info,
|
||||
Default::default(), // these tests will re-compile the builtins
|
||||
RenderTarget::Generic,
|
||||
)? {
|
||||
Monomorphized(_) => unreachable!(""),
|
||||
TypeChecked(module) => Ok(module),
|
||||
|
@ -88,8 +90,6 @@ mod test_load {
|
|||
}
|
||||
|
||||
fn multiple_modules(files: Vec<(&str, &str)>) -> Result<LoadedModule, String> {
|
||||
use roc_load_internal::file::LoadingProblem;
|
||||
|
||||
let arena = Bump::new();
|
||||
let arena = &arena;
|
||||
|
||||
|
@ -589,18 +589,18 @@ mod test_load {
|
|||
report,
|
||||
indoc!(
|
||||
"
|
||||
\u{1b}[36m── UNFINISHED LIST ─────────────────────────────────────────────────────────────\u{1b}[0m
|
||||
── UNFINISHED LIST ─────────────────────────────────────────────────────────────
|
||||
|
||||
I cannot find the end of this list:
|
||||
I cannot find the end of this list:
|
||||
|
||||
\u{1b}[36m3\u{1b}[0m\u{1b}[36m│\u{1b}[0m \u{1b}[37mmain = [\u{1b}[0m
|
||||
\u{1b}[31m^\u{1b}[0m
|
||||
3│ main = [
|
||||
^
|
||||
|
||||
You could change it to something like \u{1b}[33m[ 1, 2, 3 ]\u{1b}[0m or even just \u{1b}[33m[]\u{1b}[0m.
|
||||
Anything where there is an open and a close square bracket, and where
|
||||
the elements of the list are separated by commas.
|
||||
You could change it to something like [ 1, 2, 3 ] or even just [].
|
||||
Anything where there is an open and a close square bracket, and where
|
||||
the elements of the list are separated by commas.
|
||||
|
||||
\u{1b}[4mNote\u{1b}[0m: I may be confused by indentation"
|
||||
Note: I may be confused by indentation"
|
||||
)
|
||||
),
|
||||
Ok(_) => unreachable!("we expect failure here"),
|
||||
|
@ -769,8 +769,6 @@ mod test_load {
|
|||
];
|
||||
|
||||
let err = multiple_modules(modules).unwrap_err();
|
||||
let err = strip_ansi_escapes::strip(err).unwrap();
|
||||
let err = String::from_utf8(err).unwrap();
|
||||
assert_eq!(
|
||||
err,
|
||||
indoc!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue