Merge branch 'trunk' into task_file_adjustments

This commit is contained in:
Richard Feldman 2021-04-11 08:04:40 -04:00 committed by GitHub
commit 16d8abebbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 430 additions and 210 deletions

1
Cargo.lock generated
View File

@ -3058,6 +3058,7 @@ dependencies = [
"roc_problem",
"roc_region",
"roc_reporting",
"roc_solve",
"roc_types",
"ropey",
"serde",

View File

@ -466,6 +466,8 @@ fn link_macos(
"-lc++",
// "-lc++abi",
// "-lunwind", // TODO will eventually need this, see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
"-framework",
"Security", // This "-framework Security" arg is needed for the `rand` crate in examples/cli
// Output
"-o",
output_path.to_str().unwrap(), // app

View File

@ -14,7 +14,7 @@ use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol;
use roc_parse::ast;
use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError};
use roc_problem::can::{CycleEntry, Problem, RuntimeError};
use roc_region::all::{Located, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{Alias, Type};
@ -90,7 +90,7 @@ pub enum Declaration {
Declare(Def),
DeclareRec(Vec<Def>),
Builtin(Def),
InvalidCycle(Vec<Symbol>, Vec<(Region /* pattern */, Region /* expr */)>),
InvalidCycle(Vec<CycleEntry>),
}
impl Declaration {
@ -99,7 +99,7 @@ impl Declaration {
match self {
Declare(_) => 1,
DeclareRec(defs) => defs.len(),
InvalidCycle(_, _) => 0,
InvalidCycle { .. } => 0,
Builtin(_) => 0,
}
}
@ -530,38 +530,41 @@ pub fn sort_can_defs(
if is_invalid_cycle {
// We want to show the entire cycle in the error message, so expand it out.
let mut loc_symbols = Vec::new();
let mut entries = Vec::new();
for symbol in cycle {
match refs_by_symbol.get(&symbol) {
for symbol in &cycle {
match refs_by_symbol.get(symbol) {
None => unreachable!(
r#"Symbol `{:?}` not found in refs_by_symbol! refs_by_symbol was: {:?}"#,
symbol, refs_by_symbol
),
Some((region, _)) => {
loc_symbols.push(Located::at(*region, symbol));
let expr_region =
can_defs_by_symbol.get(&symbol).unwrap().loc_expr.region;
let entry = CycleEntry {
symbol: *symbol,
symbol_region: *region,
expr_region,
};
entries.push(entry);
}
}
}
let mut regions = Vec::with_capacity(can_defs_by_symbol.len());
for def in can_defs_by_symbol.values() {
regions.push((def.loc_pattern.region, def.loc_expr.region));
}
// Sort them by line number to make the report more helpful.
loc_symbols.sort();
regions.sort();
let symbols_in_cycle: Vec<Symbol> =
loc_symbols.into_iter().map(|s| s.value).collect();
entries.sort_by_key(|entry| entry.symbol_region);
problems.push(Problem::RuntimeError(RuntimeError::CircularDef(
symbols_in_cycle.clone(),
regions.clone(),
entries.clone(),
)));
declarations.push(Declaration::InvalidCycle(symbols_in_cycle, regions));
declarations.push(Declaration::InvalidCycle(entries));
// other groups may depend on the symbols defined here, so
// also push this cycle onto the groups
groups.push(cycle);
} else {
// slightly inefficient, because we know this becomes exactly one DeclareRec already
groups.push(cycle);
@ -590,7 +593,12 @@ pub fn sort_can_defs(
// find its successors
for succ in all_successors_without_self(symbol) {
// and add its group to the result
result.insert(symbol_to_group_index[&succ]);
match symbol_to_group_index.get(&succ) {
Some(index) => {
result.insert(*index);
}
None => unreachable!("no index for symbol {:?}", succ),
}
}
}
@ -1297,8 +1305,8 @@ fn decl_to_let(var_store: &mut VarStore, decl: Declaration, loc_ret: Located<Exp
Expr::LetNonRec(Box::new(def), Box::new(loc_ret), var_store.fresh())
}
Declaration::DeclareRec(defs) => Expr::LetRec(defs, Box::new(loc_ret), var_store.fresh()),
Declaration::InvalidCycle(symbols, regions) => {
Expr::RuntimeError(RuntimeError::CircularDef(symbols, regions))
Declaration::InvalidCycle(entries) => {
Expr::RuntimeError(RuntimeError::CircularDef(entries))
}
Declaration::Builtin(_) => {
// Builtins should only be added to top-level decls, not to let-exprs!

View File

@ -217,8 +217,8 @@ where
}
}
InvalidCycle(identifiers, _) => {
panic!("TODO gracefully handle potentially attempting to expose invalid cyclic defs {:?}" , identifiers);
InvalidCycle(entries) => {
env.problems.push(Problem::BadRecursion(entries.to_vec()));
}
Builtin(def) => {
// Builtins cannot be exposed in module declarations.
@ -289,7 +289,7 @@ where
DeclareRec(defs) => {
fix_values_captured_in_closure_defs(defs, &mut MutSet::default())
}
InvalidCycle(_, _) | Builtin(_) => {}
InvalidCycle(_) | Builtin(_) => {}
}
}

View File

@ -16,7 +16,7 @@ mod test_can {
use bumpalo::Bump;
use roc_can::expr::Expr::{self, *};
use roc_can::expr::Recursive;
use roc_problem::can::{FloatErrorKind, IntErrorKind, Problem, RuntimeError};
use roc_problem::can::{CycleEntry, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
use roc_region::all::Region;
use std::{f64, i64};
@ -907,8 +907,7 @@ mod test_can {
assert_eq!(problems, Vec::new());
let is_circular_def = if let RuntimeError(RuntimeError::CircularDef(_, _)) = loc_expr.value
{
let is_circular_def = if let RuntimeError(RuntimeError::CircularDef(_)) = loc_expr.value {
true
} else {
false
@ -936,17 +935,17 @@ mod test_can {
..
} = can_expr_with(&arena, home, src);
let is_circular_def = if let RuntimeError(RuntimeError::CircularDef(_, _)) = loc_expr.value
{
let is_circular_def = if let RuntimeError(RuntimeError::CircularDef(_)) = loc_expr.value {
true
} else {
false
};
let problem = Problem::RuntimeError(RuntimeError::CircularDef(
vec![interns.symbol(home, "x".into())],
vec![(Region::new(0, 0, 0, 1), Region::new(0, 0, 4, 5))],
));
let problem = Problem::RuntimeError(RuntimeError::CircularDef(vec![CycleEntry {
symbol: interns.symbol(home, "x".into()),
symbol_region: Region::new(0, 0, 0, 1),
expr_region: Region::new(0, 0, 4, 5),
}]));
assert_eq!(is_circular_def, true);
assert_eq!(problems, vec![problem]);
@ -972,23 +971,28 @@ mod test_can {
..
} = can_expr_with(&arena, home, src);
let problem = Problem::RuntimeError(RuntimeError::CircularDef(
vec![
interns.symbol(home, "x".into()),
interns.symbol(home, "y".into()),
interns.symbol(home, "z".into()),
],
vec![
(Region::new(0, 0, 0, 1), Region::new(0, 0, 4, 5)),
(Region::new(1, 1, 0, 1), Region::new(1, 1, 4, 5)),
(Region::new(2, 2, 0, 1), Region::new(2, 2, 4, 5)),
],
));
let problem = Problem::RuntimeError(RuntimeError::CircularDef(vec![
CycleEntry {
symbol: interns.symbol(home, "x".into()),
symbol_region: Region::new(0, 0, 0, 1),
expr_region: Region::new(0, 0, 4, 5),
},
CycleEntry {
symbol: interns.symbol(home, "y".into()),
symbol_region: Region::new(1, 1, 0, 1),
expr_region: Region::new(1, 1, 4, 5),
},
CycleEntry {
symbol: interns.symbol(home, "z".into()),
symbol_region: Region::new(2, 2, 0, 1),
expr_region: Region::new(2, 2, 4, 5),
},
]));
assert_eq!(problems, vec![problem]);
match loc_expr.value {
RuntimeError(RuntimeError::CircularDef(_, _)) => (),
RuntimeError(RuntimeError::CircularDef(_)) => (),
actual => {
panic!("Expected a CircularDef runtime error, but got {:?}", actual);
}

View File

@ -1020,7 +1020,7 @@ pub fn constrain_decls(home: ModuleId, decls: &[Declaration]) -> Constraint {
Declaration::DeclareRec(defs) => {
constraint = constrain_recursive_defs(&env, defs, constraint);
}
Declaration::InvalidCycle(_, _) => {
Declaration::InvalidCycle(_) => {
// invalid cycles give a canonicalization error. we skip them here.
continue;
}
@ -1586,7 +1586,35 @@ pub fn rec_defs_help(
})));
rigid_info.def_types.extend(def_pattern_state.headers);
}
_ => todo!(),
_ => {
let expected = annotation_expected;
let ret_constraint =
constrain_expr(env, def.loc_expr.region, &def.loc_expr.value, expected);
let def_con = And(vec![
Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: vec![],
def_types: SendMap::default(),
defs_constraint: True,
ret_constraint,
})),
// Store type into AST vars. We use Store so errors aren't reported twice
Store(signature, expr_var, std::file!(), std::line!()),
]);
rigid_info.vars.extend(&new_rigids);
rigid_info.constraints.push(Let(Box::new(LetConstraint {
rigid_vars: new_rigids,
flex_vars: def_pattern_state.vars,
def_types: SendMap::default(), // no headers introduced (at this level)
defs_constraint: def_con,
ret_constraint: True,
})));
rigid_info.def_types.extend(def_pattern_state.headers);
}
}
}
}

View File

@ -3827,8 +3827,9 @@ fn build_pending_specializations<'a>(
)
}
}
InvalidCycle(_loc_idents, _regions) => {
todo!("TODO handle InvalidCycle");
InvalidCycle(_entries) => {
// do nothing?
// this may mean the loc_symbols are not defined during codegen; is that a problem?
}
}
}

View File

@ -237,7 +237,7 @@ mod test_load {
}
}
Builtin(_) => {}
cycle @ InvalidCycle(_, _) => {
cycle @ InvalidCycle(_) => {
panic!("Unexpected cyclic def in module declarations: {:?}", cycle);
}
};

View File

@ -7,6 +7,13 @@ use roc_parse::ast::Base;
use roc_parse::pattern::PatternType;
use roc_region::all::{Located, Region};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CycleEntry {
pub symbol: Symbol,
pub symbol_region: Region,
pub expr_region: Region,
}
/// Problems that can occur in the course of canonicalization.
#[derive(Clone, Debug, PartialEq)]
pub enum Problem {
@ -24,6 +31,7 @@ pub enum Problem {
shadow: Located<Ident>,
},
CyclicAlias(Symbol, Region, Vec<Symbol>),
BadRecursion(Vec<CycleEntry>),
PhantomTypeArgument {
alias: Symbol,
variable_region: Region,
@ -141,7 +149,7 @@ pub enum RuntimeError {
},
InvalidFloat(FloatErrorKind, Region, Box<str>),
InvalidInt(IntErrorKind, Base, Region, Box<str>),
CircularDef(Vec<Symbol>, Vec<(Region /* pattern */, Region /* expr */)>),
CircularDef(Vec<CycleEntry>),
NonExhaustivePattern,

View File

@ -1,4 +1,5 @@
use roc_collections::all::MutSet;
use roc_module::ident::Lowercase;
use roc_parse::parser::{Col, Row};
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
use roc_problem::can::{FloatErrorKind, IntErrorKind, Problem, RuntimeError};
@ -29,35 +30,30 @@ pub fn can_problem<'b>(
.append(alloc.reflow(line)),
])
}
Problem::UnusedImport(module_id, region) => {
alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("Nothing from "),
alloc.module(module_id),
alloc.reflow(" is used in this module."),
]),
alloc.region(region),
alloc.concat(vec![
alloc.reflow("Since "),
alloc.module(module_id),
alloc.reflow(" isn't used, you don't need to import it."),
])
])
}
Problem::ExposedButNotDefined(symbol) => {
alloc.stack(vec![
alloc
.symbol_unqualified(symbol)
.append(alloc.reflow(" is listed as exposed, but it isn't defined in this module.")),
alloc
.reflow("You can fix this by adding a definition for ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(", or by removing it from "))
.append(alloc.keyword("exposes"))
.append(alloc.reflow("."))
])
}
Problem::UnusedImport(module_id, region) => alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("Nothing from "),
alloc.module(module_id),
alloc.reflow(" is used in this module."),
]),
alloc.region(region),
alloc.concat(vec![
alloc.reflow("Since "),
alloc.module(module_id),
alloc.reflow(" isn't used, you don't need to import it."),
]),
]),
Problem::ExposedButNotDefined(symbol) => alloc.stack(vec![
alloc.symbol_unqualified(symbol).append(
alloc.reflow(" is listed as exposed, but it isn't defined in this module."),
),
alloc
.reflow("You can fix this by adding a definition for ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(", or by removing it from "))
.append(alloc.keyword("exposes"))
.append(alloc.reflow(".")),
]),
Problem::UnusedArgument(closure_symbol, argument_symbol, region) => {
let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";
@ -79,7 +75,7 @@ pub fn can_problem<'b>(
alloc.reflow(", prefix it with an underscore, like this: \"_"),
alloc.symbol_unqualified(argument_symbol),
alloc.reflow(line),
])
]),
])
}
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => alloc
@ -174,6 +170,7 @@ pub fn can_problem<'b>(
read the guide section on phantom data.",
)),
]),
Problem::BadRecursion(entries) => to_circular_def_doc(alloc, &entries),
Problem::DuplicateRecordFieldValue {
field_name,
field_region,
@ -191,7 +188,7 @@ pub fn can_problem<'b>(
field_region,
Annotation::Error,
),
alloc.reflow("In the rest of the program, I will only use the latter definition:"),
alloc.reflow(r"In the rest of the program, I will only use the latter definition:"),
alloc.region_all_the_things(
record_region,
field_region,
@ -208,21 +205,15 @@ pub fn can_problem<'b>(
field_name,
field_region,
record_region,
} => alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("This record uses an optional value for the "),
alloc.record_field(field_name),
alloc.reflow(" field in an incorrect context!"),
]),
alloc.region_all_the_things(
} => {
return to_invalid_optional_value_report(
alloc,
filename,
field_name,
field_region,
record_region,
field_region,
field_region,
Annotation::Error,
),
alloc.reflow("You can only use optional values in record destructuring, for example in affectation:"),
alloc.reflow("{ answer ? 42, otherField } = myRecord").indent(4),
]),
);
}
Problem::DuplicateRecordFieldType {
field_name,
field_region,
@ -345,6 +336,42 @@ pub fn can_problem<'b>(
}
}
fn to_invalid_optional_value_report<'b>(
alloc: &'b RocDocAllocator<'b>,
filename: PathBuf,
field_name: Lowercase,
field_region: Region,
record_region: Region,
) -> Report {
let doc = to_invalid_optional_value_report_help(alloc, field_name, field_region, record_region);
Report {
title: "BAD OPTIONAL VALUE".to_string(),
filename,
doc,
}
}
fn to_invalid_optional_value_report_help<'b>(
alloc: &'b RocDocAllocator<'b>,
field_name: Lowercase,
field_region: Region,
record_region: Region,
) -> RocDocBuilder<'b> {
alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("This record uses an optional value for the "),
alloc.record_field(field_name),
alloc.reflow(" field in an incorrect context!"),
]),
alloc.region_all_the_things(record_region, field_region, field_region, Annotation::Error),
alloc.reflow(r"You can only use optional values in record destructuring, like:"),
alloc
.reflow(r"{ answer ? 42, otherField } = myRecord")
.indent(4),
])
}
fn to_bad_ident_expr_report<'b>(
alloc: &'b RocDocAllocator<'b>,
bad_ident: roc_parse::ident::BadIdent,
@ -672,46 +699,7 @@ fn pretty_runtime_error<'b>(
RuntimeError::LookupNotInScope(loc_name, options) => {
not_found(alloc, loc_name.region, &loc_name.value, "value", options)
}
RuntimeError::CircularDef(mut symbols, regions) => {
let first = symbols.remove(0);
if symbols.is_empty() {
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(first))
.append(alloc.reflow(
" value is defined directly in terms of itself, causing an infinite loop.",
))
// TODO "are you trying to mutate a variable?
// TODO tip?
} else {
alloc.stack(vec![
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(first))
.append(
alloc.reflow(" definition is causing a very tricky infinite loop:"),
),
alloc.region(regions[0].0),
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(first))
.append(alloc.reflow(
" value depends on itself through the following chain of definitions:",
)),
crate::report::cycle(
alloc,
4,
alloc.symbol_unqualified(first),
symbols
.into_iter()
.map(|s| alloc.symbol_unqualified(s))
.collect::<Vec<_>>(),
),
// TODO tip?
])
}
}
RuntimeError::CircularDef(entries) => to_circular_def_doc(alloc, &entries),
RuntimeError::MalformedPattern(problem, region) => {
use roc_parse::ast::Base;
use roc_problem::can::MalformedPatternProblem::*;
@ -723,7 +711,9 @@ fn pretty_runtime_error<'b>(
MalformedBase(Base::Binary) => " binary integer ",
MalformedBase(Base::Octal) => " octal integer ",
MalformedBase(Base::Decimal) => " integer ",
BadIdent(bad_ident) => return to_bad_ident_pattern_report(alloc, bad_ident, region),
BadIdent(bad_ident) => {
return to_bad_ident_pattern_report(alloc, bad_ident, region)
}
Unknown => " ",
QualifiedIdentifier => " qualified ",
};
@ -751,19 +741,20 @@ fn pretty_runtime_error<'b>(
RuntimeError::UnsupportedPattern(_) => {
todo!("unsupported patterns are currently not parsed!")
}
RuntimeError::ValueNotExposed { module_name, ident, region } => {
alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("The "),
alloc.module_name(module_name),
alloc.reflow(" module does not expose a "),
alloc.string(ident.to_string()),
alloc.reflow(" value:"),
]),
alloc.region(region),
])
}
RuntimeError::ValueNotExposed {
module_name,
ident,
region,
} => alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("The "),
alloc.module_name(module_name),
alloc.reflow(" module does not expose a "),
alloc.string(ident.to_string()),
alloc.reflow(" value:"),
]),
alloc.region(region),
]),
RuntimeError::ModuleNotImported {
module_name,
@ -777,20 +768,18 @@ fn pretty_runtime_error<'b>(
RuntimeError::MalformedIdentifier(_box_str, bad_ident, surroundings) => {
to_bad_ident_expr_report(alloc, bad_ident, surroundings)
}
RuntimeError::MalformedTypeName(_box_str, surroundings) => {
alloc.stack(vec![
alloc.reflow(r"I am confused by this type name:"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow("Type names start with an uppercase letter, "),
alloc.reflow("and can optionally be qualified by a module name, like "),
alloc.parser_suggestion("Bool"),
alloc.reflow(" or "),
alloc.parser_suggestion("Http.Request.Request"),
alloc.reflow("."),
]),
])
}
RuntimeError::MalformedTypeName(_box_str, surroundings) => alloc.stack(vec![
alloc.reflow(r"I am confused by this type name:"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow("Type names start with an uppercase letter, "),
alloc.reflow("and can optionally be qualified by a module name, like "),
alloc.parser_suggestion("Bool"),
alloc.reflow(" or "),
alloc.parser_suggestion("Http.Request.Request"),
alloc.reflow("."),
]),
]),
RuntimeError::MalformedClosure(_) => todo!(""),
RuntimeError::InvalidFloat(sign @ FloatErrorKind::PositiveInfinity, region, _raw_str)
| RuntimeError::InvalidFloat(sign @ FloatErrorKind::NegativeInfinity, region, _raw_str) => {
@ -812,7 +801,8 @@ fn pretty_runtime_error<'b>(
]),
alloc.region(region),
alloc.concat(vec![
alloc.reflow("Roc uses signed 64-bit floating points, allowing values between "),
alloc
.reflow("Roc uses signed 64-bit floating points, allowing values between "),
alloc.text(format!("{:e}", f64::MIN)),
alloc.reflow(" and "),
alloc.text(format!("{:e}", f64::MAX)),
@ -922,21 +912,7 @@ fn pretty_runtime_error<'b>(
field_name,
field_region,
record_region,
} => alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("This record uses an optional value for the "),
alloc.record_field(field_name),
alloc.reflow(" field in an incorrect context!"),
]),
alloc.region_all_the_things(
record_region,
field_region,
field_region,
Annotation::Error,
),
alloc.reflow("You can only use optional values in record destructuring, for exemple in affectation:"),
alloc.reflow("{ answer ? 42, otherField } = myRecord"),
]),
} => to_invalid_optional_value_report_help(alloc, field_name, field_region, record_region),
RuntimeError::InvalidRecordUpdate { region } => alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("This expression cannot be updated"),
@ -963,17 +939,59 @@ fn pretty_runtime_error<'b>(
region
);
}
RuntimeError::NoImplementation | RuntimeError::NoImplementationNamed { .. } => todo!("no implementation, unreachable"),
RuntimeError::NoImplementation | RuntimeError::NoImplementationNamed { .. } => {
todo!("no implementation, unreachable")
}
RuntimeError::NonExhaustivePattern => {
unreachable!("not currently reported (but can blow up at runtime)")
}
RuntimeError::ExposedButNotDefined(symbol) => alloc.stack(vec![
alloc
.symbol_unqualified(symbol)
.append(alloc.reflow(" was listed as exposed in "))
.append(alloc.module(symbol.module_id()))
.append(alloc.reflow(", but it was not defined anywhere in that module.")),
]),
RuntimeError::ExposedButNotDefined(symbol) => alloc.stack(vec![alloc
.symbol_unqualified(symbol)
.append(alloc.reflow(" was listed as exposed in "))
.append(alloc.module(symbol.module_id()))
.append(alloc.reflow(", but it was not defined anywhere in that module."))]),
}
}
fn to_circular_def_doc<'b>(
alloc: &'b RocDocAllocator<'b>,
entries: &[roc_problem::can::CycleEntry],
) -> RocDocBuilder<'b> {
// TODO "are you trying to mutate a variable?
// TODO tip?
match entries {
[] => unreachable!(),
[first] => alloc
.reflow("The ")
.append(alloc.symbol_unqualified(first.symbol))
.append(alloc.reflow(
" value is defined directly in terms of itself, causing an infinite loop.",
)),
[first, others @ ..] => {
alloc.stack(vec![
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(first.symbol))
.append(alloc.reflow(" definition is causing a very tricky infinite loop:")),
alloc.region(first.symbol_region),
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(first.symbol))
.append(alloc.reflow(
" value depends on itself through the following chain of definitions:",
)),
crate::report::cycle(
alloc,
4,
alloc.symbol_unqualified(first.symbol),
others
.iter()
.map(|s| alloc.symbol_unqualified(s.symbol))
.collect::<Vec<_>>(),
),
// TODO tip?
])
}
}
}

View File

@ -3938,7 +3938,7 @@ mod test_reporting {
),
indoc!(
r#"
SYNTAX PROBLEM
BAD OPTIONAL VALUE
This record uses an optional value for the `.y` field in an incorrect
context!
@ -3946,8 +3946,7 @@ mod test_reporting {
1 { x: 5, y ? 42 }
^^^^^^
You can only use optional values in record destructuring, for example
in affectation:
You can only use optional values in record destructuring, like:
{ answer ? 42, otherField } = myRecord
"#

View File

@ -17,9 +17,10 @@ roc_problem = { path = "../compiler/problem" }
roc_types = { path = "../compiler/types" }
roc_fmt = { path = "../compiler/fmt" }
roc_reporting = { path = "../compiler/reporting" }
roc_solve = { path = "../compiler/solve" }
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
ven_graph = { path = "../vendor/pathfinding" }
im = "15" # im and im-rc should always have the same version!
im = "15" # im and im-rc should always have the same version!
im-rc = "15" # im and im-rc should always have the same version!
bumpalo = { version = "3.2", features = ["collections"] }
inlinable_string = "0.1"
@ -69,4 +70,3 @@ harness = false
[[bench]]
name = "edit_benchmark"
harness = false

View File

@ -0,0 +1,32 @@
use crate::lang::pool::{Pool, PoolVec};
use crate::lang::{ast::Expr2, expr::Env, types::Type2};
use roc_can::expected::Expected;
use roc_module::symbol::Symbol;
use roc_region::all::Region;
use roc_types::types::Category;
#[derive(Debug)]
pub enum Constraint {
Eq(Type2, Expected<Type2>, Category, Region),
// Store(Type, Variable, &'static str, u32),
// Lookup(Symbol, Expected<Type>, Region),
// Pattern(Region, PatternCategory, Type, PExpected<Type>),
True, // Used for things that always unify, e.g. blanks and runtime errors
// SaveTheEnvironment,
// Let(Box<LetConstraint>),
// And(Vec<Constraint>),
}
pub fn constrain_expr(env: &mut Env, expr: &Expr2, expected: Expected<Type2>) -> Constraint {
use Constraint::*;
match expr {
Expr2::Str(_) => Eq(str_type(env.pool), expected, Category::Str, Region::zero()),
_ => todo!("implement constaints for {:?}", expr),
}
}
fn str_type(pool: &mut Pool) -> Type2 {
Type2::Apply(Symbol::STR_STR, PoolVec::empty(pool))
}

View File

@ -1159,7 +1159,7 @@ pub fn sort_can_defs(
}
Err((mut groups, nodes_in_cycle)) => {
let mut declarations = Vec::new();
let mut problems = Vec::new();
let problems = Vec::new();
// nodes_in_cycle are symbols that form a syntactic cycle. That isn't always a problem,
// and in general it's impossible to decide whether it is. So we use a crude heuristic:
@ -1211,7 +1211,7 @@ pub fn sort_can_defs(
}
// TODO we don't store those regions any more!
let regions = Vec::with_capacity(can_defs_by_symbol.len());
// let regions = Vec::with_capacity(can_defs_by_symbol.len());
// for def in can_defs_by_symbol.values() {
// regions.push((def.loc_pattern.region, def.loc_expr.region));
// }
@ -1220,15 +1220,16 @@ pub fn sort_can_defs(
// loc_symbols.sort();
// regions.sort();
let symbols_in_cycle: Vec<Symbol> =
loc_symbols.into_iter().map(|s| s.value).collect();
problems.push(Problem::RuntimeError(RuntimeError::CircularDef(
symbols_in_cycle.clone(),
regions.clone(),
)));
declarations.push(Declaration::InvalidCycle(symbols_in_cycle, regions));
// let symbols_in_cycle: Vec<Symbol> =
// loc_symbols.into_iter().map(|s| s.value).collect();
//
// problems.push(Problem::RuntimeError(RuntimeError::CircularDef(
// symbols_in_cycle.clone(),
// regions.clone(),
// )));
//
// declarations.push(Declaration::InvalidCycle(symbols_in_cycle, regions));
panic!("Invalid Cycle");
} else {
// slightly inefficient, because we know this becomes exactly one DeclareRec already
groups.push(cycle);

View File

@ -1,4 +1,5 @@
pub mod ast;
pub mod constrain;
mod def;
pub mod expr;
mod module;
@ -6,4 +7,4 @@ mod pattern;
pub mod pool;
pub mod roc_file;
pub mod scope;
mod types;
pub mod types;

View File

@ -254,9 +254,9 @@ impl PoolStr {
pub fn as_str(&self, pool: &Pool) -> &str {
unsafe {
let node_ptr = pool.nodes.offset(self.first_node_id.index as isize);
let node_ptr = pool.nodes.offset(self.first_node_id.index as isize) as *const u8;
let node_slice: &[u8] = &*node_ptr;
let node_slice: &[u8] = std::slice::from_raw_parts(node_ptr, self.len as usize);
std::str::from_utf8_unchecked(&node_slice[0..self.len as usize])
}

117
editor/tests/solve_expr2.rs Normal file
View File

@ -0,0 +1,117 @@
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate indoc;
use bumpalo::Bump;
use roc_can::expected::Expected;
use roc_editor::lang::{
constrain::constrain_expr,
expr::{str_to_expr2, Env},
pool::Pool,
scope::Scope,
types::Type2,
};
use roc_module::symbol::{IdentIds, ModuleIds};
use roc_region::all::Region;
use roc_solve::module::run_solve;
use roc_types::{pretty_print::content_to_string, subs::VarStore, types::Type};
fn ed_constraint_to_can_constraint(
constraint: roc_editor::lang::constrain::Constraint,
) -> roc_can::constraint::Constraint {
match constraint {
roc_editor::lang::constrain::Constraint::Eq(typ, expected, category, region) => {
let new_typ = type2_to_type(&typ);
let expected_typ = expected.get_type_ref();
let expected_typ = type2_to_type(expected_typ);
roc_can::constraint::Constraint::Eq(
new_typ,
expected.replace(expected_typ),
category,
region,
)
}
_ => todo!("{:?}", constraint),
}
}
fn type2_to_type(typ: &Type2) -> Type {
match typ {
Type2::Apply(symbol, _) => Type::Apply(*symbol, Vec::new()),
Type2::Variable(var) => Type::Variable(*var),
_ => todo!("{:?}", typ),
}
}
fn infer_eq(actual: &str, expected_str: &str) {
let mut env_pool = Pool::with_capacity(1024);
let env_arena = Bump::new();
let code_arena = Bump::new();
let mut var_store = VarStore::default();
let var = var_store.fresh();
let dep_idents = IdentIds::exposed_builtins(8);
let exposed_ident_ids = IdentIds::default();
let mut module_ids = ModuleIds::default();
let mod_id = module_ids.get_or_insert(&"ModId123".into());
let mut env = Env::new(
mod_id,
&env_arena,
&mut env_pool,
&mut var_store,
dep_idents,
&module_ids,
exposed_ident_ids,
);
let mut scope = Scope::new(env.home, env.pool, env.var_store);
let region = Region::zero();
let expr2_result = str_to_expr2(&code_arena, actual, &mut env, &mut scope, region);
match expr2_result {
Ok((expr, _)) => {
let constraint = constrain_expr(
&mut env,
&expr,
Expected::NoExpectation(Type2::Variable(var)),
);
let constraint = ed_constraint_to_can_constraint(constraint);
let (mut solved, _, _) = run_solve(
Default::default(),
Default::default(),
constraint,
var_store,
);
let mut subs = solved.inner_mut();
let content = subs.get(var).content;
let actual_str = content_to_string(content, &mut subs, mod_id, &Default::default());
assert_eq!(actual_str, expected_str);
}
Err(e) => panic!("syntax error {:?}", e),
}
}
#[test]
fn constrain_str() {
infer_eq(
indoc!(
r#"
"type inference!"
"#
),
"Str",
)
}