mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Add Task as a built-in module/type
This commit is contained in:
parent
d47a073634
commit
700c7ae9aa
86 changed files with 925 additions and 2670 deletions
|
@ -2637,12 +2637,11 @@ pub fn report_unused_imports(
|
|||
for (symbol, region) in &import.exposed_symbols {
|
||||
if !references.has_unqualified_type_or_value_lookup(*symbol)
|
||||
&& !scope.abilities_store.is_specialization_name(*symbol)
|
||||
&& !import.is_task(env)
|
||||
{
|
||||
env.problem(Problem::UnusedImport(*symbol, *region));
|
||||
}
|
||||
}
|
||||
} else if !import.is_task(env) {
|
||||
} else {
|
||||
env.problem(Problem::UnusedModuleImport(import.module_id, import.region));
|
||||
}
|
||||
}
|
||||
|
@ -2912,16 +2911,6 @@ pub struct IntroducedImport {
|
|||
exposed_symbols: Vec<(Symbol, Region)>,
|
||||
}
|
||||
|
||||
impl IntroducedImport {
|
||||
pub fn is_task(&self, env: &Env<'_>) -> bool {
|
||||
// Temporarily needed for `!` convenience. Can be removed when Task becomes a builtin.
|
||||
match env.qualified_module_ids.get_name(self.module_id) {
|
||||
Some(name) => name.as_inner().as_str() == "Task",
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn to_pending_value_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,6 @@ pub mod copy;
|
|||
pub mod def;
|
||||
mod derive;
|
||||
pub mod desugar;
|
||||
pub mod effect_module;
|
||||
pub mod env;
|
||||
pub mod exhaustive;
|
||||
pub mod expected;
|
||||
|
@ -26,6 +25,7 @@ pub mod procedure;
|
|||
pub mod scope;
|
||||
pub mod string;
|
||||
pub mod suffixed;
|
||||
pub mod task_module;
|
||||
pub mod traverse;
|
||||
|
||||
pub use derive::DERIVED_REGION;
|
||||
|
|
|
@ -3,7 +3,6 @@ use std::path::Path;
|
|||
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
|
||||
use crate::annotation::{canonicalize_annotation, AnnotationFor};
|
||||
use crate::def::{canonicalize_defs, report_unused_imports, Def};
|
||||
use crate::effect_module::HostedGeneratedFunctions;
|
||||
use crate::env::Env;
|
||||
use crate::expr::{
|
||||
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
|
||||
|
@ -23,7 +22,7 @@ use roc_parse::pattern::PatternType;
|
|||
use roc_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
||||
use roc_types::types::{AbilitySet, Alias, AliasKind, AliasVar, Type};
|
||||
use roc_types::types::{AbilitySet, Alias, Type};
|
||||
|
||||
/// The types of all exposed values/functions of a collection of modules
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -161,98 +160,6 @@ pub struct ModuleOutput {
|
|||
pub loc_dbgs: VecMap<Symbol, DbgLookup>,
|
||||
}
|
||||
|
||||
fn validate_generate_with<'a>(
|
||||
generate_with: &'a [Loc<roc_parse::header::ExposedName<'a>>],
|
||||
) -> (HostedGeneratedFunctions, Vec<Loc<Ident>>) {
|
||||
let mut functions = HostedGeneratedFunctions::default();
|
||||
let mut unknown = Vec::new();
|
||||
|
||||
for generated in generate_with {
|
||||
match generated.value.as_str() {
|
||||
"after" => functions.after = true,
|
||||
"map" => functions.map = true,
|
||||
"always" => functions.always = true,
|
||||
"loop" => functions.loop_ = true,
|
||||
"forever" => functions.forever = true,
|
||||
other => {
|
||||
// we don't know how to generate this function
|
||||
let ident = Ident::from(other);
|
||||
unknown.push(Loc::at(generated.region, ident));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(functions, unknown)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum GeneratedInfo {
|
||||
Hosted {
|
||||
effect_symbol: Symbol,
|
||||
generated_functions: HostedGeneratedFunctions,
|
||||
},
|
||||
Builtin,
|
||||
NotSpecial,
|
||||
}
|
||||
|
||||
impl GeneratedInfo {
|
||||
fn from_header_type(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
var_store: &mut VarStore,
|
||||
header_type: &HeaderType,
|
||||
) -> Self {
|
||||
match header_type {
|
||||
HeaderType::Hosted {
|
||||
generates,
|
||||
generates_with,
|
||||
name: _,
|
||||
exposes: _,
|
||||
} => {
|
||||
let name: &str = generates.into();
|
||||
let (generated_functions, unknown_generated) =
|
||||
validate_generate_with(generates_with);
|
||||
|
||||
for unknown in unknown_generated {
|
||||
env.problem(Problem::UnknownGeneratesWith(unknown));
|
||||
}
|
||||
|
||||
let effect_symbol = scope.introduce(name.into(), Region::zero()).unwrap();
|
||||
|
||||
{
|
||||
let a_var = var_store.fresh();
|
||||
|
||||
let actual =
|
||||
crate::effect_module::build_effect_actual(Type::Variable(a_var), var_store);
|
||||
|
||||
scope.add_alias(
|
||||
effect_symbol,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(AliasVar::unbound("a".into(), a_var))],
|
||||
vec![],
|
||||
actual,
|
||||
AliasKind::Opaque,
|
||||
);
|
||||
}
|
||||
|
||||
GeneratedInfo::Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
}
|
||||
}
|
||||
HeaderType::Builtin {
|
||||
generates_with,
|
||||
name: _,
|
||||
exposes: _,
|
||||
} => {
|
||||
debug_assert!(generates_with.is_empty());
|
||||
GeneratedInfo::Builtin
|
||||
}
|
||||
_ => GeneratedInfo::NotSpecial,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_no_implementation(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. }) => true,
|
||||
|
@ -320,9 +227,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
);
|
||||
}
|
||||
|
||||
let generated_info =
|
||||
GeneratedInfo::from_header_type(&mut env, &mut scope, var_store, header_type);
|
||||
|
||||
// Desugar operators (convert them to Apply calls, taking into account
|
||||
// operator precedence and associativity rules), before doing other canonicalization.
|
||||
//
|
||||
|
@ -335,7 +239,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
let mut rigid_variables = RigidVariables::default();
|
||||
|
||||
// Iniital scope values are treated like defs that appear before any others.
|
||||
// Initial scope values are treated like defs that appear before any others.
|
||||
// They include builtin types that are automatically imported, and for a platform
|
||||
// package, the required values from the app.
|
||||
//
|
||||
|
@ -495,24 +399,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
report_unused_imports(imports_introduced, &output.references, &mut env, &mut scope);
|
||||
|
||||
if let GeneratedInfo::Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
} = generated_info
|
||||
{
|
||||
let mut exposed_symbols = VecSet::default();
|
||||
|
||||
// NOTE this currently builds all functions, not just the ones that the user requested
|
||||
crate::effect_module::build_effect_builtins(
|
||||
&mut scope,
|
||||
effect_symbol,
|
||||
var_store,
|
||||
&mut exposed_symbols,
|
||||
&mut declarations,
|
||||
generated_functions,
|
||||
);
|
||||
}
|
||||
|
||||
for index in 0..declarations.len() {
|
||||
use crate::expr::DeclarationTag::*;
|
||||
|
||||
|
@ -533,8 +419,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// and which are meant to be normal definitions without a body. So for now
|
||||
// we just assume they are hosted functions (meant to be provided by the platform)
|
||||
if has_no_implementation(&declarations.expressions[index].value) {
|
||||
match generated_info {
|
||||
GeneratedInfo::Builtin => {
|
||||
match header_type {
|
||||
HeaderType::Builtin { .. } => {
|
||||
match crate::builtins::builtin_defs_map(*symbol, var_store) {
|
||||
None => {
|
||||
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
|
||||
|
@ -544,7 +430,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
GeneratedInfo::Hosted { effect_symbol, .. } => {
|
||||
HeaderType::Hosted { .. } => {
|
||||
let ident_id = symbol.ident_id();
|
||||
let ident = scope
|
||||
.locals
|
||||
|
@ -562,13 +448,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
let hosted_def = crate::effect_module::build_host_exposed_def(
|
||||
&mut scope,
|
||||
*symbol,
|
||||
&ident,
|
||||
effect_symbol,
|
||||
var_store,
|
||||
annotation,
|
||||
let hosted_def = crate::task_module::build_host_exposed_def(
|
||||
&mut scope, *symbol, &ident, var_store, annotation,
|
||||
);
|
||||
|
||||
declarations.update_builtin_def(index, hosted_def);
|
||||
|
@ -591,8 +472,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// and which are meant to be normal definitions without a body. So for now
|
||||
// we just assume they are hosted functions (meant to be provided by the platform)
|
||||
if has_no_implementation(&declarations.expressions[index].value) {
|
||||
match generated_info {
|
||||
GeneratedInfo::Builtin => {
|
||||
match header_type {
|
||||
HeaderType::Builtin { .. } => {
|
||||
match crate::builtins::builtin_defs_map(*symbol, var_store) {
|
||||
None => {
|
||||
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
|
||||
|
@ -602,7 +483,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
GeneratedInfo::Hosted { effect_symbol, .. } => {
|
||||
HeaderType::Hosted { .. } => {
|
||||
let ident_id = symbol.ident_id();
|
||||
let ident = scope
|
||||
.locals
|
||||
|
@ -620,13 +501,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
let hosted_def = crate::effect_module::build_host_exposed_def(
|
||||
&mut scope,
|
||||
*symbol,
|
||||
&ident,
|
||||
effect_symbol,
|
||||
var_store,
|
||||
annotation,
|
||||
let hosted_def = crate::task_module::build_host_exposed_def(
|
||||
&mut scope, *symbol, &ident, var_store, annotation,
|
||||
);
|
||||
|
||||
declarations.update_builtin_def(index, hosted_def);
|
||||
|
@ -652,18 +528,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
let mut aliases = MutMap::default();
|
||||
|
||||
if let GeneratedInfo::Hosted { effect_symbol, .. } = generated_info {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
// we can see if there were any
|
||||
// exposed symbols which did not have
|
||||
// corresponding defs.
|
||||
exposed_but_not_defined.remove(&effect_symbol);
|
||||
|
||||
let hosted_alias = scope.lookup_alias(effect_symbol).unwrap().clone();
|
||||
aliases.insert(effect_symbol, hosted_alias);
|
||||
}
|
||||
|
||||
for (symbol, alias) in output.aliases {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
|
|
210
crates/compiler/can/src/task_module.rs
Normal file
210
crates/compiler/can/src/task_module.rs
Normal file
|
@ -0,0 +1,210 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
|
||||
use crate::pattern::Pattern;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::SendMap;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{LambdaSet, OptAbleVar, Type};
|
||||
|
||||
pub fn build_host_exposed_def(
|
||||
scope: &mut Scope,
|
||||
symbol: Symbol,
|
||||
ident: &str,
|
||||
var_store: &mut VarStore,
|
||||
annotation: crate::annotation::Annotation,
|
||||
) -> Def {
|
||||
if symbol.contains("PlatformTask") {
|
||||
dbg!(symbol, ident, &annotation);
|
||||
}
|
||||
|
||||
let expr_var = var_store.fresh();
|
||||
let pattern = Pattern::Identifier(symbol);
|
||||
let mut pattern_vars = SendMap::default();
|
||||
pattern_vars.insert(symbol, expr_var);
|
||||
|
||||
let mut arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)> = Vec::new();
|
||||
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
||||
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
|
||||
|
||||
let crate::annotation::Annotation {
|
||||
introduced_variables,
|
||||
typ,
|
||||
aliases,
|
||||
..
|
||||
} = annotation;
|
||||
|
||||
let def_body = {
|
||||
match typ.shallow_structural_dealias() {
|
||||
Type::Function(args, _, _) => {
|
||||
for i in 0..args.len() {
|
||||
let name = format!("closure_arg_{ident}_{i}");
|
||||
|
||||
let arg_symbol = {
|
||||
let ident = name.clone().into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
};
|
||||
|
||||
let arg_var = var_store.fresh();
|
||||
|
||||
arguments.push((
|
||||
arg_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(arg_symbol)),
|
||||
));
|
||||
|
||||
captured_symbols.push((arg_symbol, arg_var));
|
||||
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var)));
|
||||
}
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{ident}");
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
ret_var: var_store.fresh(),
|
||||
};
|
||||
|
||||
let task_closure_symbol = {
|
||||
let name = format!("task_closure_{ident}");
|
||||
|
||||
let ident = name.into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
};
|
||||
|
||||
let task_closure = Expr::Closure(ClosureData {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: task_closure_symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)],
|
||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||
});
|
||||
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let body = Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
name: Symbol::TASK_TASK,
|
||||
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
|
||||
specialized_def_type,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
};
|
||||
|
||||
Expr::Closure(ClosureData {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: symbol,
|
||||
captured_symbols: std::vec::Vec::new(),
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Loc::at_zero(body)),
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// not a function
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{ident}");
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
ret_var: var_store.fresh(),
|
||||
};
|
||||
|
||||
let task_closure_symbol = {
|
||||
let name = format!("task_closure_{ident}");
|
||||
|
||||
let ident = name.into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
};
|
||||
|
||||
let task_closure = Expr::Closure(ClosureData {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: task_closure_symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)],
|
||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||
});
|
||||
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
name: Symbol::TASK_TASK,
|
||||
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
|
||||
specialized_def_type,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature: typ,
|
||||
introduced_variables,
|
||||
aliases,
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let def = Def {
|
||||
loc_pattern: Loc::at_zero(pattern),
|
||||
loc_expr: Loc::at_zero(def_body),
|
||||
expr_var,
|
||||
pattern_vars,
|
||||
annotation: Some(def_annotation),
|
||||
};
|
||||
|
||||
if symbol.contains("PlatformTask") {
|
||||
dbg!(&def);
|
||||
}
|
||||
|
||||
def
|
||||
}
|
||||
|
||||
fn build_fresh_opaque_variables(
|
||||
var_store: &mut VarStore,
|
||||
) -> (Box<Type>, Vec<OptAbleVar>, Vec<LambdaSet>) {
|
||||
let closure_var = var_store.fresh();
|
||||
|
||||
// NB: if there are bugs, check whether not introducing variables is a problem!
|
||||
// introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let a_var = var_store.fresh();
|
||||
let actual = Type::Function(
|
||||
vec![Type::EmptyRec],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(Type::Variable(a_var)),
|
||||
);
|
||||
let type_arguments = vec![OptAbleVar {
|
||||
var: a_var,
|
||||
opt_abilities: None,
|
||||
}];
|
||||
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
|
||||
|
||||
(Box::new(actual), type_arguments, lambda_set_variables)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn empty_record_pattern(var_store: &mut VarStore) -> Pattern {
|
||||
Pattern::RecordDestructure {
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
destructs: vec![],
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue