Merge pull request #2838 from rtfeldman/abilities-typechecking

Inference and checking for abilities
This commit is contained in:
Richard Feldman 2022-04-13 22:03:44 -04:00 committed by GitHub
commit 4ea4aa4708
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 2432 additions and 530 deletions

View file

@ -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"

View file

@ -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
}

View file

@ -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!(