Skip to content

Commit

Permalink
Report effectful top-level exprs
Browse files Browse the repository at this point in the history
  • Loading branch information
agu-z committed Oct 16, 2024
1 parent f8953ec commit 1cd0444
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 4 deletions.
2 changes: 1 addition & 1 deletion crates/compiler/constrain/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1845,7 +1845,7 @@ fn constrain_call_fx(
))
}
None => constraints.push_expected_type(ForReason(
Reason::CallInTopLevelDef,
Reason::CallInTopLevel,
// top-level defs are only allowed to call pure functions
constraints.push_variable(Variable::PURE),
region,
Expand Down
38 changes: 38 additions & 0 deletions crates/compiler/load/tests/test_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14552,6 +14552,44 @@ All branches in an `if` must have the same type!
"###
);

test_report!(
effect_in_top_level_value_def,
indoc!(
r#"
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
import pf.Effect
hello =
Effect.putLine! "calling hello!"
"hello"
main! = \{} ->
Effect.putLine! hello
"#
),
@r###"
── EFFECT IN TOP-LEVEL in /code/proj/Main.roc ──────────────────────────────────
This top-level expression calls an effectful function:
6│ Effect.putLine! "calling hello!"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
However, only functions are allowed to be effectful. This limitation
ensures that importing a module never produces a side effect.
Tip: If you don't need any arguments, use an empty record:
askName! : {} => Str
askName! = \{} ->
Stdout.line! "What's your name?"
Stdin.line! {}
This will allow the caller to control when the effect runs.
"###
);

test_report!(
aliased_fx_fn,
indoc!(
Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/lower_params/src/type_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ fn remove_for_reason(
}
| Reason::CrashArg
| Reason::CallInFunction(_)
| Reason::CallInTopLevelDef
| Reason::CallInTopLevel
| Reason::Stmt
| Reason::ImportParams(_) => {}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/compiler/types/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3449,7 +3449,7 @@ pub enum Reason {
},
Stmt,
CallInFunction(Option<Region>),
CallInTopLevelDef,
CallInTopLevel,
FloatLiteral,
IntLiteral,
NumLiteral,
Expand Down
21 changes: 20 additions & 1 deletion crates/reporting/src/error/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,7 +1727,26 @@ fn to_expr_report<'b>(
severity,
}
}
Reason::CallInTopLevelDef => todo!("[purity-inference] CallInTopLevelDef"),
Reason::CallInTopLevel => {
let lines = [
alloc.reflow("This top-level expression calls an effectful function:"),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("However, only functions are allowed to be effectful. This limitation ensures that importing a module never produces a side effect."),
alloc.concat([
alloc.tip(),
alloc.reflow("If you don't need any arguments, use an empty record:"),
]),
alloc.parser_suggestion(" askName! : {} => Str\n askName! = \\{} ->\n Stdout.line! \"What's your name?\"\n Stdin.line! {}"),
alloc.reflow("This will allow the caller to control when the effect runs."),
];

Report {
filename,
title: "EFFECT IN TOP-LEVEL".to_string(),
doc: alloc.stack(lines),
severity,
}
}
Reason::Stmt => todo!("[purity-inference] Stmt"),
},
}
Expand Down

0 comments on commit 1cd0444

Please sign in to comment.