Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Purity inference #7170

Merged
merged 73 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ef4eeb5
Parse effectful arrow in function annotations
agu-z Sep 17, 2024
d692fc7
Format effectful arrows in annotations
agu-z Sep 17, 2024
386a505
Add effect_type to can ClosureData
agu-z Oct 4, 2024
75177c9
Rename effect_type to fx_type and add to FunctionDef
agu-z Oct 4, 2024
3cef756
Add fx var to Type::Function et al
agu-z Oct 5, 2024
e8d7820
Add fx var to can's Call
agu-z Oct 6, 2024
7871ba1
Remove irrelevant TODO
agu-z Oct 6, 2024
5a5abe3
Unify call's fx var with that of the enclosing function
agu-z Oct 8, 2024
7776883
Unify functions fx vars
agu-z Oct 8, 2024
b9b85a2
Do not use const fx vars when canonicalizing annotations
agu-z Oct 9, 2024
7af05cc
Constrain function annotation fx to body
agu-z Oct 10, 2024
bc3ab01
Generate effectful hosted functions
agu-z Oct 14, 2024
f677592
Ignore unused fx vars in mono
agu-z Oct 14, 2024
2cce5ad
Allow unsuffixed statements in parser
agu-z Oct 14, 2024
7a7650c
Parse lowercase idents ending in `!`
agu-z Oct 24, 2024
56cdc74
Do not attempt to parse `!` suffix
agu-z Oct 15, 2024
aeeaab4
Desugar idents ending in `!` to TrySuffix
agu-z Oct 15, 2024
8bde68c
Restore parsing `!` suffix
agu-z Oct 15, 2024
fd3fb16
Add TODO to remove TryTarget::Task
agu-z Oct 15, 2024
01c9405
Detect fx mode based on hosted module
agu-z Oct 15, 2024
460fa69
Desugar stmt expr before checking whether it's suffixed
agu-z Oct 15, 2024
6e6382a
Canonicalize and constrain statement expr in purity inference mode
agu-z Oct 30, 2024
69e026f
Leftover statement warning for pure statements
agu-z Oct 16, 2024
2c85715
Get suffix from IdentId or Symbol
agu-z Oct 16, 2024
b80f447
Switch fx mode based on platform main too
agu-z Oct 16, 2024
75856ae
Support `!` in symbols provided to host
agu-z Oct 16, 2024
1da8af3
Unsuffixed effectul function warning
agu-z Oct 16, 2024
d22b2a7
Suffixed pure function warning
agu-z Oct 16, 2024
839078b
Test aliased unsuffixed effectful function
agu-z Oct 16, 2024
fd2493e
Report effect call in pure function
agu-z Oct 30, 2024
b62665e
Report effectful statement in pure function
agu-z Oct 16, 2024
f666dba
Report effectful top-level exprs
agu-z Oct 16, 2024
b01771c
Show effectful function name in mismatches
agu-z Oct 16, 2024
6adc6d9
Report ignored statement results
agu-z Oct 16, 2024
9a5a5c3
Remove irrelevant todos
agu-z Oct 16, 2024
31bc367
Ignore errors in statement checks
agu-z Oct 17, 2024
025600c
Fix non-sensical error message
agu-z Oct 17, 2024
28f35ed
Add Pure/Effectful content to checkmate
agu-z Oct 18, 2024
8a65617
Effectful function in docs
agu-z Oct 18, 2024
6533e90
Add fx to ErrorType
agu-z Oct 18, 2024
89a918c
Fix unifying pure with flex vars
agu-z Oct 22, 2024
2859829
Mark flex fx vars as pure after solving body
agu-z Oct 22, 2024
ea35094
Remove flex var case when checking symbol suffix
agu-z Oct 22, 2024
215de70
Report unsuffixed record literal field with effectful function
agu-z Oct 22, 2024
a31a351
Report suffixed pure function in literal record field
agu-z Oct 22, 2024
e75b1cf
Check suffixes of all pattern identifiers
agu-z Oct 23, 2024
2c208f9
Test tuple destructure suffixes
agu-z Oct 23, 2024
a0783c3
Test tag destructure suffixes
agu-z Oct 23, 2024
70fa4d0
Test opaque destructure suffixes
agu-z Oct 23, 2024
af6fc63
Treat untyped unsuffixed functions as pure
agu-z Oct 24, 2024
cfc4be5
Treat untyped suffixed functions as effectful
agu-z Oct 24, 2024
175a2b5
Add hint about forgetting to call a function
agu-z Oct 24, 2024
c9f001b
Allow ignored defs with an effectful RHS
agu-z Oct 24, 2024
a2f940b
Use byte literal instead of cast and ignore too_many_args
agu-z Oct 24, 2024
5f5e123
Expect only one problem in test_can::shadow_annotation
agu-z Oct 24, 2024
ca7697d
update mono tests: ids increase because of new fx vars
agu-z Oct 24, 2024
de2260e
Add simple effectful cli run tests
agu-z Oct 24, 2024
be0afbc
update ui test
agu-z Oct 30, 2024
e3c6b75
Make sure to drop suffix from symbols exposed to the host
agu-z Oct 24, 2024
2e5c143
Explicit message for StmtAfterExpr in desugar
agu-z Oct 27, 2024
b31b30c
Print fx_suffix_constraints in Debug impl for Constraints
agu-z Oct 27, 2024
dae10d1
Do not alias ClosureData.fx_type in pattern matches
agu-z Oct 27, 2024
46e808d
Refactor if-let to let-else-continue
agu-z Oct 27, 2024
a0f4b38
Update region when desugaring ! in Task mode
agu-z Oct 28, 2024
1a3d8ce
Return early when encountering `!` in an ident
agu-z Oct 28, 2024
5848a3e
Do not error when encountering EffectfulFunc in lambda_set_size
agu-z Oct 28, 2024
2fdd1ef
Add explicit error for EffectfulFunc in layout
agu-z Oct 28, 2024
935d460
Use plural 'effects' in FxInTopLevel error
agu-z Oct 30, 2024
1e835bb
Fix 'right-hand side' typo in errors
agu-z Oct 30, 2024
a4296ca
Improve wording in pattern suffix errors
agu-z Oct 30, 2024
bc0cfef
Restore UNNCESSARY DEFINITION errors for top-level defs
agu-z Oct 30, 2024
5641884
Update regions in suffixed tests
agu-z Nov 7, 2024
7f8149d
Ignore all but one cli_run effectful tests on Linux
agu-z Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 134 additions & 15 deletions crates/cli/tests/cli_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,140 @@ mod cli_run {
);
}

#[test]
#[cfg_attr(windows, ignore)]
fn effectful_form() {
test_roc_app(
"crates/cli/tests/effectful",
"form.roc",
&["Agus\n", "Zubiaga\n", "27\n"],
&[],
&[],
indoc!(
r#"
What's your first name?
What's your last name?

Hi, Agus Zubiaga!

How old are you?

Nice! You can vote!

Bye! 👋
"#
),
UseValgrind::No,
TestCliCommands::Dev,
);
}

// Other effectful tests are disabled in Linux because of a platform build issue
// The program actually runs fine, but it fails on tests

#[test]
#[cfg_attr(windows, ignore)]
#[cfg_attr(target_os = "linux", ignore)]
fn interactive_effects() {
test_roc_app(
"examples/cli",
"effects.roc",
&["hi there!"],
&[],
&[],
"hi there!\nIt is known\n",
UseValgrind::Yes,
TestCliCommands::Run,
)
}

#[test]
#[cfg_attr(windows, ignore)]
#[cfg_attr(target_os = "linux", ignore)]
fn effectful_hello() {
test_roc_app(
"crates/cli/tests/effectful",
"hello.roc",
&[],
&[],
&[],
indoc!(
r#"
I'm an effect 👻
"#
),
UseValgrind::No,
TestCliCommands::Dev,
);
}

#[test]
#[cfg_attr(windows, ignore)]
#[cfg_attr(target_os = "linux", ignore)]
fn effectful_loops() {
test_roc_app(
"crates/cli/tests/effectful",
"loops.roc",
&[],
&[],
&[],
indoc!(
r#"
Lu
Marce
Joaquin
Chloé
Mati
Pedro
"#
),
UseValgrind::No,
TestCliCommands::Dev,
);
}

#[test]
#[cfg_attr(windows, ignore)]
#[cfg_attr(target_os = "linux", ignore)]
fn effectful_untyped_passed_fx() {
test_roc_app(
"crates/cli/tests/effectful",
"untyped_passed_fx.roc",
&[],
&[],
&[],
indoc!(
r#"
Before hello
Hello, World!
After hello
"#
),
UseValgrind::No,
TestCliCommands::Dev,
);
}

#[test]
#[cfg_attr(windows, ignore)]
#[cfg_attr(target_os = "linux", ignore)]
fn effectful_ignore_result() {
test_roc_app(
"crates/cli/tests/effectful",
"ignore_result.roc",
&[],
&[],
&[],
indoc!(
r#"
I asked for input and I ignored it. Deal with it! 😎
"#
),
UseValgrind::No,
TestCliCommands::Dev,
);
}

#[test]
#[cfg_attr(windows, ignore)]
fn transitive_expects() {
Expand Down Expand Up @@ -1144,21 +1278,6 @@ mod cli_run {
assert_valid_roc_check_status(out.status);
}

#[test]
#[cfg_attr(windows, ignore)]
fn interactive_effects() {
test_roc_app(
"examples/cli",
"effects.roc",
&["hi there!"],
&[],
&[],
"hi there!\nIt is known\n",
UseValgrind::Yes,
TestCliCommands::Run,
)
}

#[test]
#[cfg_attr(windows, ignore)]
// tea = The Elm Architecture
Expand Down
27 changes: 27 additions & 0 deletions crates/cli/tests/effectful/form.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" }

import pf.Effect

main! : {} => {}
main! = \{} ->
first = ask! "What's your first name?"
last = ask! "What's your last name?"

Effect.putLine! "\nHi, $(first) $(last)!\n"

when Str.toU8 (ask! "How old are you?") is
Err InvalidNumStr ->
Effect.putLine! "Enter a valid number"

Ok age if age >= 18 ->
Effect.putLine! "\nNice! You can vote!"

Ok age ->
Effect.putLine! "\nYou'll be able to vote in $(Num.toStr (18 - age)) years"

Effect.putLine! "\nBye! 👋"

ask! : Str => Str
ask! = \question ->
Effect.putLine! question
Effect.getLine! {}
7 changes: 7 additions & 0 deletions crates/cli/tests/effectful/hello.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" }

import pf.Effect

main! : {} => {}
main! = \{} ->
Effect.putLine! "I'm an effect 👻"
8 changes: 8 additions & 0 deletions crates/cli/tests/effectful/ignore_result.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" }

import pf.Effect

main! : {} => {}
main! = \{} ->
_ = Effect.getLine! {}
Effect.putLine! "I asked for input and I ignored it. Deal with it! 😎"
16 changes: 16 additions & 0 deletions crates/cli/tests/effectful/loops.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" }

import pf.Effect

main! : {} => {}
main! = \{} ->
friends = ["Lu", "Marce", "Joaquin", "Chloé", "Mati", "Pedro"]
printAll! friends

printAll! : List Str => {}
printAll! = \friends ->
when friends is
[] -> {}
[first, .. as remaining] ->
Effect.putLine! first
printAll! remaining
12 changes: 12 additions & 0 deletions crates/cli/tests/effectful/untyped_passed_fx.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" }

import pf.Effect

main! : {} => {}
main! = \{} ->
logged! "hello" (\{} -> Effect.putLine! "Hello, World!")

logged! = \name, fx! ->
Effect.putLine! "Before $(name)"
fx! {}
Effect.putLine! "After $(name)"
4 changes: 2 additions & 2 deletions crates/compiler/builtins/roc/Task.roc
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ sequence = \taskList ->
Task.loop (taskList, List.withCapacity (List.len taskList)) \(tasks, values) ->
when tasks is
[task, .. as rest] ->
value = task!
Task.ok (Step (rest, List.append values value))
Task.map task \value ->
Step (rest, List.append values value)

[] ->
Task.ok (Done values)
Expand Down
15 changes: 11 additions & 4 deletions crates/compiler/can/src/annotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use crate::scope::{PendingAbilitiesInScope, Scope, SymbolLookup};
use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_parse::ast::{AssignedField, ExtractSpaces, Pattern, Tag, TypeAnnotation, TypeHeader};
use roc_parse::ast::{
AssignedField, ExtractSpaces, FunctionArrow, Pattern, Tag, TypeAnnotation, TypeHeader,
};
use roc_problem::can::ShadowKind;
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
Expand Down Expand Up @@ -448,7 +450,7 @@ pub fn find_type_def_symbols(
stack.push(&t.value);
}
}
Function(arguments, result) => {
Function(arguments, _arrow, result) => {
for t in arguments.iter() {
stack.push(&t.value);
}
Expand Down Expand Up @@ -554,7 +556,7 @@ fn can_annotation_help(
use roc_parse::ast::TypeAnnotation::*;

match annotation {
Function(argument_types, return_type) => {
Function(argument_types, arrow, return_type) => {
let mut args = Vec::new();

for arg in *argument_types {
Expand Down Expand Up @@ -589,7 +591,12 @@ fn can_annotation_help(
introduced_variables.insert_lambda_set(lambda_set);
let closure = Type::Variable(lambda_set);

Type::Function(args, Box::new(closure), Box::new(ret))
let fx_type = match arrow {
FunctionArrow::Pure => Type::Pure,
FunctionArrow::Effectful => Type::Effectful,
};

Type::Function(args, Box::new(closure), Box::new(ret), Box::new(fx_type))
}
Apply(module_name, ident, type_arguments) => {
let symbol = match make_apply_symbol(env, region, scope, module_name, ident, references)
Expand Down
3 changes: 3 additions & 0 deletions crates/compiler/can/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ fn defn(
expr_var: var_store.fresh(),
pattern_vars: SendMap::default(),
annotation: None,
kind: crate::def::DefKind::Let,
}
}

Expand Down Expand Up @@ -446,6 +447,7 @@ fn defn_help(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: ret_var,
fx_type: Variable::PURE,
early_returns: vec![],
name: fn_name,
captured_symbols: Vec::new(),
Expand Down Expand Up @@ -547,6 +549,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel)
expr_var: record_var,
pattern_vars: SendMap::default(),
annotation: None,
kind: crate::def::DefKind::Let,
};

let body = LetNonRec(Box::new(def), Box::new(no_region(cont)));
Expand Down
Loading