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

Open
wants to merge 67 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
8c5f7e5
Parse effectful arrow in function annotations
agu-z Sep 17, 2024
1c471d5
Format effectful arrows in annotations
agu-z Sep 17, 2024
a3a27b1
Add effect_type to can ClosureData
agu-z Oct 4, 2024
c8c4f74
Rename effect_type to fx_type and add to FunctionDef
agu-z Oct 4, 2024
1b2d328
Add fx var to Type::Function et al
agu-z Oct 5, 2024
748e815
Add fx var to can's Call
agu-z Oct 6, 2024
9fe9b21
Remove irrelevant TODO
agu-z Oct 6, 2024
6278d88
Unify call's fx var with that of the enclosing function
agu-z Oct 8, 2024
b1de015
Unify functions fx vars
agu-z Oct 8, 2024
f86b6aa
Do not use const fx vars when canonicalizing annotations
agu-z Oct 9, 2024
92b0476
Constrain function annotation fx to body
agu-z Oct 10, 2024
8267b49
Generate effectful hosted functions
agu-z Oct 14, 2024
b071820
Ignore unused fx vars in mono
agu-z Oct 14, 2024
0c1fe4f
Allow unsuffixed statements in parser
agu-z Oct 14, 2024
3a385f9
Parse lowercase idents ending in `!`
agu-z Oct 24, 2024
70cab56
Do not attempt to parse `!` suffix
agu-z Oct 15, 2024
92cbf4d
Desugar idents ending in `!` to TrySuffix
agu-z Oct 15, 2024
4582c04
Restore parsing `!` suffix
agu-z Oct 15, 2024
2cb6659
Add TODO to remove TryTarget::Task
agu-z Oct 15, 2024
9632b9c
Detect fx mode based on hosted module
agu-z Oct 15, 2024
a4b2529
Desugar stmt expr before checking whether it's suffixed
agu-z Oct 15, 2024
c700a82
Canonicalize and constrain statement expr in purity inference mode
agu-z Oct 16, 2024
5a6eadd
Leftover statement warning for pure statements
agu-z Oct 16, 2024
47db890
Get suffix from IdentId or Symbol
agu-z Oct 16, 2024
dfaf97c
Switch fx mode based on platform main too
agu-z Oct 16, 2024
28e41f8
Support `!` in symbols provided to host
agu-z Oct 16, 2024
f74b2d2
Unsuffixed effectul function warning
agu-z Oct 16, 2024
74b7e6f
Suffixed pure function warning
agu-z Oct 16, 2024
005dad0
Test aliased unsuffixed effectful function
agu-z Oct 16, 2024
7ac9ee5
Report effect call in pure function
agu-z Oct 16, 2024
fc8ecd0
Report effectful statement in pure function
agu-z Oct 16, 2024
8921208
Report effectful top-level exprs
agu-z Oct 16, 2024
f3c4edf
Show effectful function name in mismatches
agu-z Oct 16, 2024
58c2461
Report ignored statement results
agu-z Oct 16, 2024
ac5a59f
Remove irrelevant todos
agu-z Oct 16, 2024
079f4d1
Ignore errors in statement checks
agu-z Oct 17, 2024
8e47180
Fix non-sensical error message
agu-z Oct 17, 2024
0834ec1
Add Pure/Effectful content to checkmate
agu-z Oct 18, 2024
1218e12
Effectful function in docs
agu-z Oct 18, 2024
442e781
Add fx to ErrorType
agu-z Oct 18, 2024
c4b5062
Fix unifying pure with flex vars
agu-z Oct 22, 2024
20c367f
Mark flex fx vars as pure after solving body
agu-z Oct 22, 2024
9912263
Remove flex var case when checking symbol suffix
agu-z Oct 22, 2024
a093b34
Report unsuffixed record literal field with effectful function
agu-z Oct 22, 2024
665e15e
Report suffixed pure function in literal record field
agu-z Oct 22, 2024
062b55b
Check suffixes of all pattern identifiers
agu-z Oct 23, 2024
62be61f
Test tuple destructure suffixes
agu-z Oct 23, 2024
e4269e6
Test tag destructure suffixes
agu-z Oct 23, 2024
b299656
Test opaque destructure suffixes
agu-z Oct 23, 2024
ffe3fb5
Treat untyped unsuffixed functions as pure
agu-z Oct 24, 2024
8b9cb44
Treat untyped suffixed functions as effectful
agu-z Oct 24, 2024
4e54ad5
Add hint about forgetting to call a function
agu-z Oct 24, 2024
0ffbc41
Allow ignored defs with an effectful RHS
agu-z Oct 24, 2024
503127f
Use byte literal instead of cast and ignore too_many_args
agu-z Oct 24, 2024
60a57d7
Expect only one problem in test_can::shadow_annotation
agu-z Oct 24, 2024
d698706
update mono tests: ids increase because of new fx vars
agu-z Oct 24, 2024
725179a
Add simple effectful cli run tests
agu-z Oct 24, 2024
740f827
update ui test
agu-z Oct 24, 2024
ee94d81
Make sure to drop suffix from symbols exposed to the host
agu-z Oct 24, 2024
c831e3b
Explicit message for StmtAfterExpr in desugar
agu-z Oct 27, 2024
2f907ca
Print fx_suffix_constraints in Debug impl for Constraints
agu-z Oct 27, 2024
bdebf11
Do not alias ClosureData.fx_type in pattern matches
agu-z Oct 27, 2024
b1dde59
Refactor if-let to let-else-continue
agu-z Oct 27, 2024
ed8f8d6
Update region when desugaring ! in Task mode
agu-z Oct 28, 2024
15fd942
Return early when encountering `!` in an ident
agu-z Oct 28, 2024
3718c53
Do not error when encountering EffectfulFunc in lambda_set_size
agu-z Oct 28, 2024
6dbf5a3
Add explicit error for EffectfulFunc in layout
agu-z Oct 28, 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
111 changes: 111 additions & 0 deletions crates/cli/tests/cli_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,117 @@ mod cli_run {
);
}

#[test]
#[cfg_attr(windows, 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)]
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,
);
}

#[test]
#[cfg_attr(windows, 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)]
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)]
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
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,
name: fn_name,
captured_symbols: Vec::new(),
recursive: Recursive::NotRecursive,
Expand Down Expand Up @@ -546,6 +548,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