implement standard as patterns in mono IR

This commit is contained in:
Folkert 2023-01-11 14:12:53 +01:00
parent 726d8d80c4
commit cf15654ee5
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
7 changed files with 347 additions and 128 deletions

View file

@ -521,138 +521,142 @@ fn tests_at_path<'a>(
unique
}
fn test_for_pattern<'a>(pattern: &Pattern<'a>) -> Option<Test<'a>> {
use Pattern::*;
use Test::*;
let test = match pattern {
Identifier(_) | Underscore => {
return None;
}
As(subpattern, _) => return test_for_pattern(subpattern),
RecordDestructure(destructs, _) => {
// not rendered, so pick the easiest
let union = Union {
render_as: RenderAs::Tag,
alternatives: vec![Ctor {
tag_id: TagId(0),
name: CtorName::Tag(TagName(RECORD_TAG_NAME.into())),
arity: destructs.len(),
}],
};
let mut arguments = std::vec::Vec::new();
for destruct in destructs {
match &destruct.typ {
DestructType::Guard(guard) => {
arguments.push((guard.clone(), destruct.layout));
}
DestructType::Required(_) => {
arguments.push((Pattern::Underscore, destruct.layout));
}
}
}
IsCtor {
tag_id: 0,
ctor_name: CtorName::Tag(TagName(RECORD_TAG_NAME.into())),
union,
arguments,
}
}
NewtypeDestructure {
tag_name,
arguments,
} => {
let tag_id = 0;
let union = Union::newtype_wrapper(CtorName::Tag(tag_name.clone()), arguments.len());
IsCtor {
tag_id,
ctor_name: CtorName::Tag(tag_name.clone()),
union,
arguments: arguments.to_vec(),
}
}
AppliedTag {
tag_name,
tag_id,
arguments,
union,
..
} => IsCtor {
tag_id: *tag_id,
ctor_name: CtorName::Tag(tag_name.clone()),
union: union.clone(),
arguments: arguments.to_vec(),
},
List {
arity,
element_layout: _,
elements: _,
} => IsListLen {
bound: match arity {
ListArity::Exact(_) => ListLenBound::Exact,
ListArity::Slice(_, _) => ListLenBound::AtLeast,
},
len: arity.min_len() as _,
},
Voided { .. } => internal_error!("unreachable"),
OpaqueUnwrap { opaque, argument } => {
let union = Union {
render_as: RenderAs::Tag,
alternatives: vec![Ctor {
tag_id: TagId(0),
name: CtorName::Opaque(*opaque),
arity: 1,
}],
};
IsCtor {
tag_id: 0,
ctor_name: CtorName::Opaque(*opaque),
union,
arguments: vec![(**argument).clone()],
}
}
BitLiteral { value, .. } => IsBit(*value),
EnumLiteral { tag_id, union, .. } => IsByte {
tag_id: *tag_id as _,
num_alts: union.alternatives.len(),
},
IntLiteral(v, precision) => IsInt(*v, *precision),
FloatLiteral(v, precision) => IsFloat(*v, *precision),
DecimalLiteral(v) => IsDecimal(*v),
StrLiteral(v) => IsStr(v.clone()),
};
Some(test)
}
fn test_at_path<'a>(
selected_path: &[PathInstruction],
branch: &Branch<'a>,
) -> Option<GuardedTest<'a>> {
use Pattern::*;
use Test::*;
match branch
let (_, pattern) = branch
.patterns
.iter()
.find(|(path, _)| path == selected_path)
{
None => None,
Some((_, pattern)) => {
let test = match pattern {
Identifier(_) | Underscore => {
if let Guard::Guard { .. } = &branch.guard {
// no tests for this pattern remain, but we cannot discard it yet
// because it has a guard!
return Some(GuardedTest::Placeholder);
} else {
return None;
}
}
.find(|(path, _)| path == selected_path)?;
RecordDestructure(destructs, _) => {
// not rendered, so pick the easiest
let union = Union {
render_as: RenderAs::Tag,
alternatives: vec![Ctor {
tag_id: TagId(0),
name: CtorName::Tag(TagName(RECORD_TAG_NAME.into())),
arity: destructs.len(),
}],
};
let mut arguments = std::vec::Vec::new();
for destruct in destructs {
match &destruct.typ {
DestructType::Guard(guard) => {
arguments.push((guard.clone(), destruct.layout));
}
DestructType::Required(_) => {
arguments.push((Pattern::Underscore, destruct.layout));
}
}
}
IsCtor {
tag_id: 0,
ctor_name: CtorName::Tag(TagName(RECORD_TAG_NAME.into())),
union,
arguments,
}
}
NewtypeDestructure {
tag_name,
arguments,
} => {
let tag_id = 0;
let union =
Union::newtype_wrapper(CtorName::Tag(tag_name.clone()), arguments.len());
IsCtor {
tag_id,
ctor_name: CtorName::Tag(tag_name.clone()),
union,
arguments: arguments.to_vec(),
}
}
AppliedTag {
tag_name,
tag_id,
arguments,
union,
..
} => IsCtor {
tag_id: *tag_id,
ctor_name: CtorName::Tag(tag_name.clone()),
union: union.clone(),
arguments: arguments.to_vec(),
},
List {
arity,
element_layout: _,
elements: _,
} => IsListLen {
bound: match arity {
ListArity::Exact(_) => ListLenBound::Exact,
ListArity::Slice(_, _) => ListLenBound::AtLeast,
},
len: arity.min_len() as _,
},
Voided { .. } => internal_error!("unreachable"),
OpaqueUnwrap { opaque, argument } => {
let union = Union {
render_as: RenderAs::Tag,
alternatives: vec![Ctor {
tag_id: TagId(0),
name: CtorName::Opaque(*opaque),
arity: 1,
}],
};
IsCtor {
tag_id: 0,
ctor_name: CtorName::Opaque(*opaque),
union,
arguments: vec![(**argument).clone()],
}
}
BitLiteral { value, .. } => IsBit(*value),
EnumLiteral { tag_id, union, .. } => IsByte {
tag_id: *tag_id as _,
num_alts: union.alternatives.len(),
},
IntLiteral(v, precision) => IsInt(*v, *precision),
FloatLiteral(v, precision) => IsFloat(*v, *precision),
DecimalLiteral(v) => IsDecimal(*v),
StrLiteral(v) => IsStr(v.clone()),
};
let guarded_test = GuardedTest::TestNotGuarded { test };
Some(guarded_test)
match test_for_pattern(pattern) {
Some(test) => Some(GuardedTest::TestNotGuarded { test }),
None => {
if let Guard::Guard { .. } = &branch.guard {
// no tests for this pattern remain, but we cannot discard it yet
// because it has a guard!
Some(GuardedTest::Placeholder)
} else {
None
}
}
}
}
@ -732,6 +736,10 @@ fn to_relevant_branch_help<'a>(
match pattern {
Identifier(_) | Underscore => Some(branch.clone()),
As(subpattern, _symbol) => {
to_relevant_branch_help(test, path, start, end, branch, *subpattern)
}
RecordDestructure(destructs, _) => match test {
IsCtor {
ctor_name: test_name,
@ -1099,6 +1107,8 @@ fn needs_tests(pattern: &Pattern) -> bool {
match pattern {
Identifier(_) | Underscore => false,
As(subpattern, _) => needs_tests(subpattern),
NewtypeDestructure { .. }
| RecordDestructure(..)
| AppliedTag { .. }

View file

@ -7411,6 +7411,28 @@ fn store_pattern_help<'a>(
// do nothing
return StorePattern::NotProductive(stmt);
}
As(subpattern, symbol) => {
let stored_subpattern =
store_pattern_help(env, procs, layout_cache, subpattern, outer_symbol, stmt);
let mut stmt = match stored_subpattern {
StorePattern::Productive(stmt) => stmt,
StorePattern::NotProductive(stmt) => stmt,
};
// An identifier in a pattern can define at most one specialization!
// Remove any requested specializations for this name now, since this is the definition site.
let specialization_symbol = procs
.symbol_specializations
.remove_single(*symbol)
// Can happen when the symbol was never used under this body, and hence has no
// requested specialization.
.unwrap_or(*symbol);
substitute_in_exprs(env.arena, &mut stmt, specialization_symbol, outer_symbol);
return StorePattern::Productive(stmt);
}
IntLiteral(_, _)
| FloatLiteral(_, _)
| DecimalLiteral(_)
@ -9259,6 +9281,7 @@ fn call_specialized_proc<'a>(
pub enum Pattern<'a> {
Identifier(Symbol),
Underscore,
As(Box<Pattern<'a>>, Symbol),
IntLiteral([u8; 16], IntWidth),
FloatLiteral(u64, FloatWidth),
DecimalLiteral([u8; 16]),
@ -9316,6 +9339,7 @@ impl<'a> Pattern<'a> {
| Pattern::BitLiteral { .. }
| Pattern::EnumLiteral { .. }
| Pattern::StrLiteral(_) => { /* terminal */ }
Pattern::As(subpattern, _) => stack.push(subpattern),
Pattern::RecordDestructure(destructs, _) => {
for destruct in destructs {
match &destruct.typ {
@ -9394,7 +9418,12 @@ fn from_can_pattern_help<'a>(
match can_pattern {
Underscore => Ok(Pattern::Underscore),
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
As(_, _) => todo!(),
As(subpattern, symbol) => {
let mono_subpattern =
from_can_pattern_help(env, procs, layout_cache, &subpattern.value, assignments)?;
Ok(Pattern::As(Box::new(mono_subpattern), *symbol))
}
AbilityMemberSpecialization { ident, .. } => Ok(Pattern::Identifier(*ident)),
IntLiteral(var, _, int_str, int, _bound) => Ok(make_num_literal_pattern(
env,

View file

@ -4145,3 +4145,63 @@ fn issue_4712() {
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn pattern_as_toplevel() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
record = { a: 42i64, b: "foo" }
main =
when record is
{ a: 42i64 } as r -> record == r
_ -> Bool.false
"#
),
true,
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn pattern_as_nested() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
record = { a: 42i64, b: "foo" }
main =
when Pair {} record is
Pair {} ({ a: 42i64 } as r) -> record == r
_ -> Bool.false
"#
),
true,
bool
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn pattern_as_of_symbol() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main =
when "foo" is
a as b -> a == b
"#
),
true,
bool
);
}

View file

@ -0,0 +1,37 @@
procedure Bool.1 ():
let Bool.24 : Int1 = false;
ret Bool.24;
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
dec #Attr.3;
dec #Attr.2;
ret Bool.23;
procedure Test.1 ():
let Test.9 : I64 = 42i64;
let Test.10 : Str = "foo";
let Test.8 : {I64, Str} = Struct {Test.9, Test.10};
ret Test.8;
procedure Test.0 ():
let Test.18 : {} = Struct {};
let Test.19 : {I64, Str} = CallByName Test.1;
let Test.5 : {{I64, Str}, {}} = Struct {Test.19, Test.18};
let Test.14 : {I64, Str} = StructAtIndex 0 Test.5;
inc Test.14;
let Test.15 : I64 = StructAtIndex 0 Test.14;
dec Test.14;
let Test.16 : I64 = 42i64;
let Test.17 : Int1 = lowlevel Eq Test.16 Test.15;
if Test.17 then
let Test.13 : {I64, Str} = StructAtIndex 0 Test.5;
inc Test.13;
dec Test.5;
let Test.7 : {I64, Str} = CallByName Test.1;
let Test.6 : Int1 = CallByName Bool.11 Test.7 Test.13;
ret Test.6;
else
dec Test.5;
let Test.11 : Int1 = CallByName Bool.1;
ret Test.11;

View file

@ -0,0 +1,9 @@
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.23;
procedure Test.0 ():
let Test.3 : Str = "foo";
let Test.4 : Int1 = CallByName Bool.11 Test.3 Test.3;
dec Test.3;
ret Test.4;

View file

@ -0,0 +1,29 @@
procedure Bool.1 ():
let Bool.24 : Int1 = false;
ret Bool.24;
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
dec #Attr.3;
dec #Attr.2;
ret Bool.23;
procedure Test.1 ():
let Test.8 : I64 = 42i64;
let Test.9 : Str = "foo";
let Test.7 : {I64, Str} = Struct {Test.8, Test.9};
ret Test.7;
procedure Test.0 ():
let Test.4 : {I64, Str} = CallByName Test.1;
let Test.11 : I64 = StructAtIndex 0 Test.4;
let Test.12 : I64 = 42i64;
let Test.13 : Int1 = lowlevel Eq Test.12 Test.11;
if Test.13 then
let Test.6 : {I64, Str} = CallByName Test.1;
let Test.5 : Int1 = CallByName Bool.11 Test.6 Test.4;
ret Test.5;
else
dec Test.4;
let Test.10 : Int1 = CallByName Bool.1;
ret Test.10;

View file

@ -2217,7 +2217,7 @@ fn issue_4749() {
expect
input = [82, 111, 99]
got = Decode.fromBytes input Json.fromUtf8
got = Decode.fromBytes input Json.fromUtf8
got == Ok "Roc"
"###
)
@ -2246,7 +2246,7 @@ fn lambda_set_with_imported_toplevels_issue_4733() {
fn order_list_size_tests_issue_4732() {
indoc!(
r###"
when [] is
when [] is
[1, ..] -> "B1"
[2, 1, ..] -> "B2"
[3, 2, 1, ..] -> "B3"
@ -2363,3 +2363,48 @@ fn nullable_wrapped_with_nullable_not_last_index() {
"###
)
}
#[mono_test]
fn pattern_as_toplevel() {
indoc!(
r###"
app "test" provides [main] to "./platform"
record = { a: 42i64, b: "foo" }
main =
when record is
{ a: 42i64 } as r -> record == r
_ -> Bool.false
"###
)
}
#[mono_test]
fn pattern_as_nested() {
indoc!(
r###"
app "test" provides [main] to "./platform"
record = { a: 42i64, b: "foo" }
main =
when Pair {} record is
Pair {} ({ a: 42i64 } as r) -> record == r
_ -> Bool.false
"###
)
}
#[mono_test]
fn pattern_as_of_symbol() {
indoc!(
r###"
app "test" provides [main] to "./platform"
main =
when "foo" is
a as b -> a == b
"###
)
}