Merge branch 'trunk' into edit_big_string

This commit is contained in:
Richard Feldman 2021-04-13 08:36:56 -04:00 committed by GitHub
commit a1a08fbb8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1841 additions and 49 deletions

1
Cargo.lock generated
View File

@ -3060,6 +3060,7 @@ dependencies = [
"roc_reporting",
"roc_solve",
"roc_types",
"roc_unify",
"ropey",
"serde",
"snafu",

View File

@ -89,4 +89,16 @@ impl<T> Expected<T> {
}
}
}
pub fn replace_ref<U>(&self, new: U) -> Expected<U> {
match self {
Expected::NoExpectation(_val) => Expected::NoExpectation(new),
Expected::ForReason(reason, _val, region) => {
Expected::ForReason(reason.clone(), new, *region)
}
Expected::FromAnnotation(pattern, size, source, _val) => {
Expected::FromAnnotation(pattern.clone(), *size, *source, new)
}
}
}
}

View File

@ -409,6 +409,18 @@ pub fn canonicalize_expr<'a>(
ast::Expr::Var { module_name, ident } => {
canonicalize_lookup(env, scope, module_name, ident, region)
}
ast::Expr::Underscore(name) => {
// we parse underscores, but they are not valid expression syntax
let problem = roc_problem::can::RuntimeError::MalformedIdentifier(
(*name).into(),
roc_parse::ident::BadIdent::Underscore(region.start_line, region.start_col),
region,
);
env.problem(Problem::RuntimeError(problem.clone()));
(RuntimeError(problem), Output::default())
}
ast::Expr::Defs(loc_defs, loc_ret) => {
can_defs_with_return(
env,

View File

@ -119,6 +119,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
| Str(_)
| AccessorFunction(_)
| Var { .. }
| Underscore { .. }
| MalformedIdent(_, _)
| MalformedClosure
| PrecedenceConflict { .. }

View File

@ -198,7 +198,7 @@ pub fn canonicalize_pattern<'a>(
Underscore(_) => match pattern_type {
WhenBranch | FunctionArg => Pattern::Underscore,
ptype => unsupported_pattern(env, ptype, region),
TopLevelDef | DefExpr => bad_underscore(env, region),
},
NumLiteral(string) => match pattern_type {
@ -402,7 +402,21 @@ pub fn canonicalize_pattern<'a>(
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern {
env.problem(Problem::UnsupportedPattern(pattern_type, region));
use roc_problem::can::BadPattern;
env.problem(Problem::UnsupportedPattern(
BadPattern::Unsupported(pattern_type),
region,
));
Pattern::UnsupportedPattern(region)
}
fn bad_underscore(env: &mut Env, region: Region) -> Pattern {
use roc_problem::can::BadPattern;
env.problem(Problem::UnsupportedPattern(
BadPattern::UnderscoreInDef,
region,
));
Pattern::UnsupportedPattern(region)
}

View File

@ -30,6 +30,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
| Access(_, _)
| AccessorFunction(_)
| Var { .. }
| Underscore { .. }
| MalformedIdent(_, _)
| MalformedClosure
| GlobalTag(_)
@ -189,6 +190,10 @@ impl<'a> Formattable<'a> for Expr<'a> {
buf.push_str(ident);
}
Underscore(name) => {
buf.push('_');
buf.push_str(name);
}
Apply(loc_expr, loc_args, _) => {
if apply_needs_parens {
buf.push('(');

View File

@ -115,6 +115,8 @@ pub enum Expr<'a> {
ident: &'a str,
},
Underscore(&'a str),
// Tags
GlobalTag(&'a str),
PrivateTag(&'a str),

View File

@ -192,6 +192,30 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> {
)
}
/// In some contexts we want to parse the `_` as an expression, so it can then be turned into a
/// pattern later
fn parse_loc_term_or_underscore<'a>(
min_indent: u16,
options: ExprParseOptions,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
one_of!(
loc_expr_in_parens_etc_help(min_indent),
loc!(specialize(EExpr::Str, string_literal_help())),
loc!(specialize(EExpr::Number, positive_number_literal_help())),
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
loc!(underscore_expression()),
loc!(record_literal_help(min_indent)),
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
loc!(map_with_arena!(
assign_or_destructure_identifier(),
ident_to_expr
)),
)
.parse(arena, state)
}
fn parse_loc_term<'a>(
min_indent: u16,
options: ExprParseOptions,
@ -213,6 +237,26 @@ fn parse_loc_term<'a>(
.parse(arena, state)
}
fn underscore_expression<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
move |arena: &'a Bump, state: State<'a>| {
let (_, _, next_state) = word1(b'_', EExpr::Underscore).parse(arena, state)?;
let lowercase_ident_expr = {
let row = state.line;
let col = state.column;
specialize(move |_, _, _| EExpr::End(row, col), lowercase_ident())
};
let (_, output, final_state) = optional(lowercase_ident_expr).parse(arena, next_state)?;
match output {
Some(name) => Ok((MadeProgress, Expr::Underscore(name), final_state)),
None => Ok((MadeProgress, Expr::Underscore(&""), final_state)),
}
}
}
fn loc_possibly_negative_or_negated_term<'a>(
min_indent: u16,
options: ExprParseOptions,
@ -243,10 +287,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
)
}
)),
|arena, state| {
// TODO use parse_loc_term_better
parse_loc_term(min_indent, options, arena, state)
}
|arena, state| { parse_loc_term_or_underscore(min_indent, options, arena, state) }
]
}
@ -1316,6 +1357,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
Ok(Pattern::QualifiedIdentifier { module_name, ident })
}
}
Expr::Underscore(opt_name) => Ok(Pattern::Underscore(opt_name)),
Expr::GlobalTag(value) => Ok(Pattern::GlobalTag(value)),
Expr::PrivateTag(value) => Ok(Pattern::PrivateTag(value)),
Expr::Apply(loc_val, loc_args, _) => {

View File

@ -411,6 +411,7 @@ pub enum EExpr<'a> {
If(If<'a>, Row, Col),
Lambda(ELambda<'a>, Row, Col),
Underscore(Row, Col),
InParens(EInParens<'a>, Row, Col),
Record(ERecord<'a>, Row, Col),

View File

@ -1573,7 +1573,7 @@ mod test_parse {
#[test]
fn single_underscore_closure() {
let arena = Bump::new();
let pattern = Located::new(0, 0, 1, 2, Underscore(&""));
let pattern = Located::new(0, 0, 1, 2, Pattern::Underscore(&""));
let patterns = &[pattern];
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
let actual = parse_expr_with(&arena, "\\_ -> 42");
@ -1635,8 +1635,8 @@ mod test_parse {
#[test]
fn closure_with_underscores() {
let arena = Bump::new();
let underscore1 = Located::new(0, 0, 1, 2, Underscore(&""));
let underscore2 = Located::new(0, 0, 4, 9, Underscore(&"name"));
let underscore1 = Located::new(0, 0, 1, 2, Pattern::Underscore(&""));
let underscore2 = Located::new(0, 0, 4, 9, Pattern::Underscore(&"name"));
let patterns = bumpalo::vec![in &arena; underscore1, underscore2];
let expected = Closure(
arena.alloc(patterns),
@ -1866,6 +1866,49 @@ mod test_parse {
);
}
#[test]
fn underscore_backpassing() {
let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline, Newline];
let underscore = Located::new(1, 1, 0, 1, Pattern::Underscore(&""));
let identifier_y = Located::new(1, 1, 7, 8, Identifier("y"));
let num_4 = Num("4");
let var_y = Var {
module_name: "",
ident: "y",
};
let loc_var_y = arena.alloc(Located::new(1, 1, 12, 13, var_y));
let closure = ParensAround(arena.alloc(Closure(arena.alloc([identifier_y]), loc_var_y)));
let loc_closure = Located::new(1, 1, 5, 14, closure);
let ret = Expr::SpaceBefore(arena.alloc(num_4), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 1, ret);
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
let expected = Expr::SpaceBefore(
arena.alloc(Expr::Backpassing(
arena.alloc([underscore]),
arena.alloc(loc_closure),
arena.alloc(loc_ret),
)),
reset_indentation.into_bump_slice(),
);
assert_parses_to(
indoc!(
r#"# leading comment
_ <- (\y -> y)
4
"#
),
expected,
);
}
#[test]
fn two_backpassing() {
let arena = Bump::new();
@ -2624,7 +2667,7 @@ mod test_parse {
guard: None,
});
let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
let pattern2 = Pattern::SpaceBefore(arena.alloc(Pattern::Underscore("")), newlines);
let loc_pattern2 = Located::new(3, 3, 4, 5, pattern2);
let expr2 = Num("4");
let loc_expr2 = Located::new(3, 3, 9, 10, expr2);
@ -2661,7 +2704,7 @@ mod test_parse {
let branch1 = {
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::Underscore("")), newlines);
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
let num_1 = Num("1");
let expr1 = Located::new(
@ -2680,7 +2723,8 @@ mod test_parse {
};
let branch2 = {
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), &[Newline, Newline]);
let pattern1 =
Pattern::SpaceBefore(arena.alloc(Pattern::Underscore("")), &[Newline, Newline]);
let loc_pattern1 = Located::new(4, 4, 4, 5, pattern1);
let num_1 = Num("2");
let expr1 = Located::new(
@ -3576,7 +3620,7 @@ mod test_parse {
guard: None,
});
let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore(&"")), newlines);
let pattern2 = Pattern::SpaceBefore(arena.alloc(Pattern::Underscore(&"")), newlines);
let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2);
let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 9, 10, expr2);
@ -3621,7 +3665,7 @@ mod test_parse {
guard: None,
});
let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore(&"")), newlines);
let pattern2 = Pattern::SpaceBefore(arena.alloc(Pattern::Underscore(&"")), newlines);
let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2);
let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 9, 10, expr2);

View File

@ -14,6 +14,12 @@ pub struct CycleEntry {
pub expr_region: Region,
}
#[derive(Clone, Debug, PartialEq)]
pub enum BadPattern {
UnderscoreInDef,
Unsupported(PatternType),
}
/// Problems that can occur in the course of canonicalization.
#[derive(Clone, Debug, PartialEq)]
pub enum Problem {
@ -25,7 +31,7 @@ pub enum Problem {
UnusedArgument(Symbol, Symbol, Region),
PrecedenceProblem(PrecedenceProblem),
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
UnsupportedPattern(PatternType, Region),
UnsupportedPattern(BadPattern, Region),
ShadowingInAnnotation {
original_region: Region,
shadow: Located<Ident>,

View File

@ -2,7 +2,7 @@ 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};
use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
use roc_region::all::Region;
use std::path::PathBuf;
@ -103,7 +103,11 @@ pub fn can_problem<'b>(
},
alloc.region(region),
]),
Problem::UnsupportedPattern(pattern_type, region) => {
Problem::UnsupportedPattern(BadPattern::UnderscoreInDef, region) => alloc.stack(vec![
alloc.reflow("Underscore patterns are not allowed in definitions"),
alloc.region(region),
]),
Problem::UnsupportedPattern(BadPattern::Unsupported(pattern_type), region) => {
use roc_parse::pattern::PatternType::*;
let this_thing = match pattern_type {

View File

@ -6334,4 +6334,27 @@ mod test_reporting {
),
)
}
#[test]
fn underscore_let() {
report_problem_as(
indoc!(
r#"
_ = 3
4
"#
),
indoc!(
r#"
SYNTAX PROBLEM
Underscore patterns are not allowed in definitions
1 _ = 3
^
"#
),
)
}
}

View File

@ -858,7 +858,7 @@ pub enum PReason {
OptionalField,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnnotationSource {
TypedIfBranch { index: Index, num_branches: usize },
TypedWhenBranch { index: Index },

View File

@ -15,6 +15,7 @@ roc_region = { path = "../compiler/region" }
roc_module = { path = "../compiler/module" }
roc_problem = { path = "../compiler/problem" }
roc_types = { path = "../compiler/types" }
roc_unify = { path = "../compiler/unify" }
roc_fmt = { path = "../compiler/fmt" }
roc_reporting = { path = "../compiler/reporting" }
roc_solve = { path = "../compiler/solve" }

View File

@ -7,4 +7,5 @@ mod pattern;
pub mod pool;
pub mod roc_file;
pub mod scope;
pub mod solve;
pub mod types;

View File

@ -153,7 +153,7 @@ pub fn to_pattern2<'a>(
Underscore(_) => match pattern_type {
WhenBranch | FunctionArg => Pattern2::Underscore,
ptype => unsupported_pattern(env, ptype, region),
TopLevelDef | DefExpr => underscore_in_def(env, region),
},
FloatLiteral(ref string) => match pattern_type {
@ -521,7 +521,21 @@ fn unsupported_pattern<'a>(
pattern_type: PatternType,
region: Region,
) -> Pattern2 {
env.problem(Problem::UnsupportedPattern(pattern_type, region));
use roc_problem::can::BadPattern;
env.problem(Problem::UnsupportedPattern(
BadPattern::Unsupported(pattern_type),
region,
));
Pattern2::UnsupportedPattern(region)
}
fn underscore_in_def<'a>(env: &mut Env<'a>, region: Region) -> Pattern2 {
use roc_problem::can::BadPattern;
env.problem(Problem::UnsupportedPattern(
BadPattern::UnderscoreInDef,
region,
));
Pattern2::UnsupportedPattern(region)
}

1595
editor/src/lang/solve.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,45 +5,50 @@ extern crate indoc;
use bumpalo::Bump;
use roc_can::expected::Expected;
use roc_collections::all::MutMap;
use roc_editor::lang::solve;
use roc_editor::lang::{
constrain::constrain_expr,
constrain::Constraint,
expr::{str_to_expr2, Env},
pool::Pool,
scope::Scope,
types::Type2,
};
use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol;
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};
use roc_types::solved_types::Solved;
use roc_types::subs::{Subs, Variable};
use roc_types::{pretty_print::content_to_string, subs::VarStore};
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();
fn run_solve(
mempool: &mut Pool,
aliases: MutMap<Symbol, roc_types::types::Alias>,
rigid_variables: MutMap<Variable, Lowercase>,
constraint: Constraint,
var_store: VarStore,
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
let env = solve::Env {
vars_by_symbol: MutMap::default(),
aliases,
};
let expected_typ = type2_to_type(expected_typ);
let mut subs = Subs::new(var_store.into());
roc_can::constraint::Constraint::Eq(
new_typ,
expected.replace(expected_typ),
category,
region,
)
}
_ => todo!("{:?}", constraint),
for (var, name) in rigid_variables {
subs.rigid_var(var, name);
}
}
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),
}
// Now that the module is parsed, canonicalized, and constrained,
// we need to type check it.
let mut problems = Vec::new();
// Run the solver to populate Subs.
let (solved_subs, solved_env) = solve::run(mempool, &env, &mut problems, subs, &constraint);
(solved_subs, solved_env, problems)
}
fn infer_eq(actual: &str, expected_str: &str) {
@ -83,20 +88,29 @@ fn infer_eq(actual: &str, expected_str: &str) {
Expected::NoExpectation(Type2::Variable(var)),
);
let constraint = ed_constraint_to_can_constraint(constraint);
let Env {
pool,
var_store: ref_var_store,
..
} = env;
// extract the var_store out of the env again
let mut var_store = VarStore::default();
std::mem::swap(ref_var_store, &mut var_store);
let (mut solved, _, _) = run_solve(
pool,
Default::default(),
Default::default(),
constraint,
var_store,
);
let mut subs = solved.inner_mut();
let subs = solved.inner_mut();
let content = subs.get(var).content;
let actual_str = content_to_string(content, &mut subs, mod_id, &Default::default());
let actual_str = content_to_string(content, &subs, mod_id, &Default::default());
assert_eq!(actual_str, expected_str);
}

View File

@ -5,7 +5,7 @@ app "http-get"
main : Task.Task {} *
main =
{} <- await (Stdout.line "What URL should I get?")
_ <- await (Stdout.line "What URL should I get?")
url <- await Stdin.line