mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
Merging in remote
This commit is contained in:
commit
3984a8d60e
14 changed files with 2781 additions and 643 deletions
|
@ -215,7 +215,7 @@ asc : Int a, Int a -> [ Eq, Lt, Gt ]
|
||||||
##
|
##
|
||||||
desc : Int a, Int a -> [ Eq, Lt, Gt ]
|
desc : Int a, Int a -> [ Eq, Lt, Gt ]
|
||||||
|
|
||||||
## TODO should we offer hash32 etc even if it has to do a hash64 and truncate?
|
## TODO should we offer hash32 etc even if someday it has to do a hash64 and truncate?
|
||||||
##
|
##
|
||||||
## CAUTION: This function may give different answers in future releases of Roc,
|
## CAUTION: This function may give different answers in future releases of Roc,
|
||||||
## so be aware that if you rely on the exact answer this gives today, your
|
## so be aware that if you rely on the exact answer this gives today, your
|
||||||
|
@ -224,16 +224,16 @@ hash64 : a -> U64
|
||||||
|
|
||||||
## Limits
|
## Limits
|
||||||
|
|
||||||
## The highest number that can be stored in an #Int without overflowing its
|
## The highest number that can be stored in an #I32 without overflowing its
|
||||||
## available memory and crashing.
|
## available memory and crashing.
|
||||||
##
|
##
|
||||||
## Note that this is smaller than the positive version of #Int.lowest,
|
## Note that this is smaller than the positive version of #Int.lowestI32
|
||||||
## which means if you call #Num.abs on #Int.lowest, it will overflow and crash!
|
## which means if you call #Num.abs on #Int.lowestI32, it will overflow and crash!
|
||||||
highest : Int *
|
highestI32 : I32
|
||||||
|
|
||||||
## The lowest number that can be stored in an #Int without overflowing its
|
## The lowest number that can be stored in an #I32 without overflowing its
|
||||||
## available memory and crashing.
|
## available memory and crashing.
|
||||||
##
|
##
|
||||||
## Note that the positive version of this number is this is larger than
|
## Note that the positive version of this number is this is larger than
|
||||||
## #Int.highest, which means if you call #Num.abs on #Int.lowest, it will overflow and crash!
|
## #Int.highestI32, which means if you call #Num.abs on #Int.lowestI32, it will overflow and crash!
|
||||||
lowest : Int *
|
lowest : I32
|
||||||
|
|
|
@ -2,12 +2,58 @@ api Str provides Str, isEmpty, join
|
||||||
|
|
||||||
## Types
|
## Types
|
||||||
|
|
||||||
## A sequence of [UTF-8](https://en.wikipedia.org/wiki/UTF-8) text characters.
|
## A [Unicode](https://unicode.org) text value.
|
||||||
##
|
##
|
||||||
## One #Str can be up to 2 gigabytes in size. If you need to store larger
|
## Dealing with text is deep topic, so by design, Roc's `Str` module sticks
|
||||||
## strings than that, you can split them into smaller chunks and operate
|
## to the basics. For more advanced use cases like working with raw [code points](https://unicode.org/glossary/#code_point),
|
||||||
## on those instead of on one large #Str. This often runs faster in practice,
|
## see the [roc/unicode](roc/unicode) package, and for locale-specific text
|
||||||
## even for strings much smaller than 2 gigabytes.
|
## functions (including capitalization, as capitalization rules vary by locale)
|
||||||
|
## see the [roc/locale](roc/locale) package.
|
||||||
|
##
|
||||||
|
## ### Unicode
|
||||||
|
##
|
||||||
|
## Unicode can represent text values which span multiple languages, symbols, and emoji.
|
||||||
|
## Here are some valid Roc strings:
|
||||||
|
##
|
||||||
|
## * "Roc"
|
||||||
|
## * "鹏"
|
||||||
|
## * "🐦"
|
||||||
|
##
|
||||||
|
## Every Unicode string is a sequence of [grapheme clusters](https://unicode.org/glossary/#grapheme_cluster).
|
||||||
|
## A grapheme cluster corresponds to what a person reading a string might call
|
||||||
|
## a "character", but because the term "character" is used to mean many different
|
||||||
|
## concepts across different programming languages, we intentionally avoid it in Roc.
|
||||||
|
## Instead, we use the term "clusters" as a shorthand for "grapheme clusters."
|
||||||
|
##
|
||||||
|
## You can get the number of grapheme clusters in a string by calling `Str.countClusters` on it:
|
||||||
|
##
|
||||||
|
## >>> Str.countClusters "Roc"
|
||||||
|
##
|
||||||
|
## >>> Str.countClusters "音乐"
|
||||||
|
##
|
||||||
|
## >>> Str.countClusters "👍"
|
||||||
|
##
|
||||||
|
## > The `countClusters` function traverses the entire string to calculate its answer,
|
||||||
|
## > so it's much better for performance to use `Str.isEmpty` instead of
|
||||||
|
## > calling `Str.countClusters` and checking whether the count was `0`.
|
||||||
|
##
|
||||||
|
## ### Escape characters
|
||||||
|
##
|
||||||
|
## ### String interpolation
|
||||||
|
##
|
||||||
|
## ### Encoding
|
||||||
|
##
|
||||||
|
## Roc strings are not coupled to any particular
|
||||||
|
## [encoding](https://en.wikipedia.org/wiki/Character_encoding). As it happens,
|
||||||
|
## they are currently encoded in UTF-8, but this module is intentionally designed
|
||||||
|
## not to rely on that implementation detail so that a future release of Roc can
|
||||||
|
## potentially change it without breaking existing Roc applications.
|
||||||
|
##
|
||||||
|
## This module has functions to can convert a #Str to a #List of raw [code unit](https://unicode.org/glossary/#code_unit)
|
||||||
|
## integers (not to be confused with the [code points](https://unicode.org/glossary/#code_point)
|
||||||
|
## mentioned earlier) in a particular encoding. If you need encoding-specific functions,
|
||||||
|
## you should take a look at the [roc/unicode](roc/unicode) package.
|
||||||
|
## It has many more tools than this module does!
|
||||||
Str : [ @Str ]
|
Str : [ @Str ]
|
||||||
|
|
||||||
## Convert
|
## Convert
|
||||||
|
@ -21,10 +67,22 @@ Str : [ @Str ]
|
||||||
## but it's recommended to pass much smaller numbers instead.
|
## but it's recommended to pass much smaller numbers instead.
|
||||||
##
|
##
|
||||||
## Passing a negative number for decimal places is equivalent to passing 0.
|
## Passing a negative number for decimal places is equivalent to passing 0.
|
||||||
decimal : Int, Float -> Str
|
decimal : Float *, ULen -> Str
|
||||||
|
|
||||||
## Convert an #Int to a string.
|
## Convert an #Int to a string.
|
||||||
int : Float -> Str
|
int : Int * -> Str
|
||||||
|
|
||||||
|
## Split a string around a separator.
|
||||||
|
##
|
||||||
|
## >>> Str.splitClusters "1,2,3" ","
|
||||||
|
##
|
||||||
|
## Passing `""` for the separator is not useful; it returns the original string
|
||||||
|
## wrapped in a list.
|
||||||
|
##
|
||||||
|
## >>> Str.splitClusters "1,2,3" ""
|
||||||
|
##
|
||||||
|
## To split a string into its grapheme clusters, use #Str.clusters
|
||||||
|
split : Str, Str -> List Str
|
||||||
|
|
||||||
## Check
|
## Check
|
||||||
|
|
||||||
|
@ -58,4 +116,196 @@ padStart : Str, Int, Str -> Str
|
||||||
|
|
||||||
padEnd : Str, Int, Str -> Str
|
padEnd : Str, Int, Str -> Str
|
||||||
|
|
||||||
|
## Grapheme Clusters
|
||||||
|
|
||||||
|
## Split a string into its grapheme clusters.
|
||||||
|
##
|
||||||
|
## >>> Str.clusters "1,2,3"
|
||||||
|
##
|
||||||
|
## >>> Str.clusters "👍👍👍"
|
||||||
|
##
|
||||||
|
clusters : Str -> List Str
|
||||||
|
|
||||||
|
reverseClusters : Str -> Str
|
||||||
|
|
||||||
|
foldClusters : Str, { start: state, step: (state, Str -> state) } -> state
|
||||||
|
|
||||||
|
## Returns #True if the string begins with a capital letter, and #False otherwise.
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized "hi"
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized "Hi"
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized " Hi"
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized "Česká"
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized "Э"
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized "東京"
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized "🐦"
|
||||||
|
##
|
||||||
|
## >>> Str.isCapitalized ""
|
||||||
|
##
|
||||||
|
## Since the rules for how to capitalize an uncapitalized string vary by locale,
|
||||||
|
## see the [roc/locale](roc/locale) package for functions which do that.
|
||||||
|
isCapitalized : Str -> Bool
|
||||||
|
|
||||||
|
## ## Code Units
|
||||||
|
##
|
||||||
|
## Besides grapheme clusters, another way to break down strings is into
|
||||||
|
## raw code unit integers.
|
||||||
|
##
|
||||||
|
## Code units are no substitute for grapheme clusters!
|
||||||
|
## These functions exist to support advanced use cases like those found in
|
||||||
|
## [roc/unicode](roc/unicode), and using code units when grapheme clusters would
|
||||||
|
## be more appropriate can very easily lead to bugs.
|
||||||
|
##
|
||||||
|
## For example, `Str.countGraphemes "👩👩👦👦"` returns `1`,
|
||||||
|
## whereas `Str.toUtf8 "👩👩👦👦"` returns a list with a length of 25,
|
||||||
|
## `Str.toUtf16 "👩👩👦👦"` returns a list with a length of 11.
|
||||||
|
## and `Str.toUtf32 "👩👩👦👦"` returns a list with a length of 7.
|
||||||
|
|
||||||
|
## Return a #List of the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit).
|
||||||
|
## (To split the string into a #List of smaller #Str values instead of #U8 values,
|
||||||
|
## see #Str.split and #Str.clusters.)
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf8 "👩👩👦👦"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf8 "Roc"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf8 "鹏"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf8 "🐦"
|
||||||
|
##
|
||||||
|
## For a more flexible function that walks through each of these #U8 code units
|
||||||
|
## without creating a #List, see #Str.foldUtf8 and #Str.foldRevUtf8.
|
||||||
|
toUtf8 : Str -> List U8
|
||||||
|
|
||||||
|
## Return a #List of the string's #U16 UTF-16 [code units](https://unicode.org/glossary/#code_unit).
|
||||||
|
## (To split the string into a #List of smaller #Str values instead of #U16 values,
|
||||||
|
## see #Str.split and #Str.clusters.)
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf16 "👩👩👦👦"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf16 "Roc"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf16 "鹏"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf16 "🐦"
|
||||||
|
##
|
||||||
|
## For a more flexible function that walks through each of these #U16 code units
|
||||||
|
## without creating a #List, see #Str.foldUtf16 and #Str.foldRevUtf16.
|
||||||
|
toUtf16 : Str -> List U16
|
||||||
|
|
||||||
|
## Return a #List of the string's #U32 UTF-32 [code units](https://unicode.org/glossary/#code_unit).
|
||||||
|
## (To split the string into a #List of smaller #Str values instead of #U32 values,
|
||||||
|
## see #Str.split and #Str.clusters.)
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf32 "👩👩👦👦"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf32 "Roc"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf32 "鹏"
|
||||||
|
##
|
||||||
|
## >>> Str.toUtf32 "🐦"
|
||||||
|
##
|
||||||
|
## For a more flexible function that walks through each of these #U32 code units
|
||||||
|
## without creating a #List, see #Str.foldUtf32 and #Str.foldRevUtf32.
|
||||||
|
toUtf32 : Str -> List U32
|
||||||
|
|
||||||
|
|
||||||
|
## Walk through the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit)
|
||||||
|
## to build up a state.
|
||||||
|
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.foldClusters.)
|
||||||
|
##
|
||||||
|
## Here are the #U8 values that will be passed to `step` when this function is
|
||||||
|
## called on various strings:
|
||||||
|
##
|
||||||
|
## * `"👩👩👦👦"` passes 240, 159, 145, 169, 226, 128, 141, 240, 159, 145, 169, 226, 128, 141, 240, 159, 145, 166, 226, 128, 141, 240, 159, 145, 166
|
||||||
|
## * `"Roc"` passes 82, 111, 99
|
||||||
|
## * `"鹏"` passes 233, 185, 143
|
||||||
|
## * `"🐦"` passes 240, 159, 144, 166
|
||||||
|
##
|
||||||
|
## To convert a #Str into a plain `List U8` of UTF-8 code units, see #Str.toUtf8.
|
||||||
|
foldUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||||
|
|
||||||
|
## Walk through the string's #U16 UTF-16 [code units](https://unicode.org/glossary/#code_unit)
|
||||||
|
## to build up a state.
|
||||||
|
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.foldClusters.)
|
||||||
|
##
|
||||||
|
## Here are the #U16 values that will be passed to `step` when this function is
|
||||||
|
## called on various strings:
|
||||||
|
##
|
||||||
|
## * `"👩👩👦👦"` passes 55357, 56425, 8205, 55357, 56425, 8205, 55357, 56422, 8205, 55357, 56422
|
||||||
|
## * `"Roc"` passes 82, 111, 99
|
||||||
|
## * `"鹏"` passes 40527
|
||||||
|
## * `"🐦"` passes 55357, 56358
|
||||||
|
##
|
||||||
|
## To convert a #Str into a plain `List U16` of UTF-16 code units, see #Str.toUtf16.
|
||||||
|
foldUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||||
|
|
||||||
|
## Walk through the string's #U32 UTF-32 [code units](https://unicode.org/glossary/#code_unit)
|
||||||
|
## to build up a state.
|
||||||
|
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.foldClusters.)
|
||||||
|
##
|
||||||
|
## Here are the #U32 values that will be passed to `step` when this function is
|
||||||
|
## called on various strings:
|
||||||
|
##
|
||||||
|
## * `"👩👩👦👦"` passes 128105, 8205, 128105, 8205, 128102, 8205, 128102
|
||||||
|
## * `"Roc"` passes 82, 111, 99
|
||||||
|
## * `"鹏"` passes 40527
|
||||||
|
## * `"🐦"` passes 128038
|
||||||
|
##
|
||||||
|
## To convert a #Str into a plain `List U32` of UTF-32 code units, see #Str.toUtf32.
|
||||||
|
foldUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
|
||||||
|
|
||||||
|
|
||||||
|
## Walk backwards through the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit)
|
||||||
|
## to build up a state.
|
||||||
|
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.foldClusters.)
|
||||||
|
##
|
||||||
|
## Here are the #U8 values that will be passed to `step` when this function is
|
||||||
|
## called on various strings:
|
||||||
|
##
|
||||||
|
## * `"👩👩👦👦"` passes 166, 145, 159, 240, 141, 128, 226, 166, 145, 159, 240, 141, 128, 226, 169, 145, 159, 240, 141, 128, 226, 169, 145, 159, 240
|
||||||
|
## * `"Roc"` passes 99, 111, 82
|
||||||
|
## * `"鹏"` passes 143, 185, 233
|
||||||
|
## * `"🐦"` passes 166, 144, 159, 240
|
||||||
|
##
|
||||||
|
## To convert a #Str into a plain `List U8` of UTF-8 code units, see #Str.toUtf8.
|
||||||
|
foldRevUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||||
|
|
||||||
|
## Walk backwards through the string's #U16 UTF-16 [code units](https://unicode.org/glossary/#code_unit)
|
||||||
|
## to build up a state.
|
||||||
|
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.foldClusters.)
|
||||||
|
##
|
||||||
|
## Here are the #U16 values that will be passed to `step` when this function is
|
||||||
|
## called on various strings:
|
||||||
|
##
|
||||||
|
## * `"👩👩👦👦"` passes 56422, 55357, 8205, 56422, 55357, 8205, 56425, 55357, 8205, 56425, 55357
|
||||||
|
## * `"Roc"` passes 99, 111, 82
|
||||||
|
## * `"鹏"` passes 40527
|
||||||
|
## * `"🐦"` passes 56358, 55357
|
||||||
|
##
|
||||||
|
## To convert a #Str into a plain `List U16` of UTF-16 code units, see #Str.toUtf16.
|
||||||
|
foldRevUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||||
|
|
||||||
|
## Walk backwards through the string's #U32 UTF-32 [code units](https://unicode.org/glossary/#code_unit)
|
||||||
|
## to build up a state.
|
||||||
|
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.foldClusters.)
|
||||||
|
##
|
||||||
|
## Here are the #U32 values that will be passed to `step` when this function is
|
||||||
|
## called on various strings:
|
||||||
|
##
|
||||||
|
## * `"👩👩👦👦"` passes 128102, 8205, 128102, 8205, 128105, 8205, 128105
|
||||||
|
## * `"Roc"` passes 99, 111, 82
|
||||||
|
## * `"鹏"` passes 40527
|
||||||
|
## * `"🐦"` passes 128038
|
||||||
|
##
|
||||||
|
## To convert a #Str into a plain `List U32` of UTF-32 code units, see #Str.toUtf32.
|
||||||
|
foldRevUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
) -> Value {
|
) -> Value {
|
||||||
use roc_mono::expr::Expr::*;
|
use roc_mono::expr::Expr::*;
|
||||||
|
|
||||||
|
let ptr_bytes = env.cfg.pointer_bytes() as u32;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Int(num) => builder.ins().iconst(types::I64, *num),
|
Int(num) => builder.ins().iconst(types::I64, *num),
|
||||||
Float(num) => builder.ins().f64const(*num),
|
Float(num) => builder.ins().f64const(*num),
|
||||||
|
@ -173,7 +175,6 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
},
|
},
|
||||||
Struct(sorted_fields) => {
|
Struct(sorted_fields) => {
|
||||||
let cfg = env.cfg;
|
let cfg = env.cfg;
|
||||||
let ptr_bytes = cfg.pointer_bytes() as u32;
|
|
||||||
|
|
||||||
// The slot size will be the sum of all the fields' sizes
|
// The slot size will be the sum of all the fields' sizes
|
||||||
let mut slot_size = 0;
|
let mut slot_size = 0;
|
||||||
|
@ -189,6 +190,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
));
|
));
|
||||||
|
|
||||||
// Create instructions for storing each field's expression
|
// Create instructions for storing each field's expression
|
||||||
|
// NOTE assumes that all fields have the same width!
|
||||||
for (index, (field_expr, field_layout)) in sorted_fields.iter().enumerate() {
|
for (index, (field_expr, field_layout)) in sorted_fields.iter().enumerate() {
|
||||||
let val = build_expr(env, &scope, module, builder, field_expr, procs);
|
let val = build_expr(env, &scope, module, builder, field_expr, procs);
|
||||||
|
|
||||||
|
@ -203,6 +205,50 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
.ins()
|
.ins()
|
||||||
.stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
|
.stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
|
||||||
}
|
}
|
||||||
|
Tag { tag_layout, arguments , tag_id, union_size, .. } => {
|
||||||
|
let cfg = env.cfg;
|
||||||
|
let ptr_bytes = cfg.pointer_bytes() as u32;
|
||||||
|
|
||||||
|
// NOTE: all variants of a tag union must have the same size, so (among other things)
|
||||||
|
// it's easy to quickly index them in arrays. Therefore the size of this tag doens't
|
||||||
|
// depend on the tag arguments, but solely on the layout of the whole tag union
|
||||||
|
let slot_size = tag_layout.stack_size(ptr_bytes);
|
||||||
|
|
||||||
|
// Create a slot
|
||||||
|
let slot = builder.create_stack_slot(StackSlotData::new(
|
||||||
|
StackSlotKind::ExplicitSlot,
|
||||||
|
slot_size
|
||||||
|
));
|
||||||
|
|
||||||
|
// Create instructions for storing each field's expression
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
// still need to insert the tag discriminator for non-single unions
|
||||||
|
// when there are no arguments, e.g. `Nothing : Maybe a`
|
||||||
|
if *union_size > 1 {
|
||||||
|
let val = builder.ins().iconst(types::I64, *tag_id as i64);
|
||||||
|
builder.ins().stack_store(val, slot, Offset32::new(0));
|
||||||
|
offset += ptr_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (field_expr, field_layout) in arguments.iter() {
|
||||||
|
let val = build_expr(env, &scope, module, builder, field_expr, procs);
|
||||||
|
|
||||||
|
let field_size = field_layout.stack_size(ptr_bytes);
|
||||||
|
let field_offset = i32::try_from(offset)
|
||||||
|
.expect("TODO handle field size conversion to i32");
|
||||||
|
|
||||||
|
builder.ins().stack_store(val, slot, Offset32::new(field_offset));
|
||||||
|
|
||||||
|
offset += field_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
|
||||||
|
}
|
||||||
|
|
||||||
Access {
|
Access {
|
||||||
label,
|
label,
|
||||||
field_layout,
|
field_layout,
|
||||||
|
@ -252,15 +298,49 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
.ins()
|
.ins()
|
||||||
.load(cfg.pointer_type(), mem_flags, record, Offset32::new(offset))
|
.load(cfg.pointer_type(), mem_flags, record, Offset32::new(offset))
|
||||||
}
|
}
|
||||||
|
AccessAtIndex {
|
||||||
|
index,
|
||||||
|
field_layouts,
|
||||||
|
expr,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let cfg = env.cfg;
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (field_index, field_layout) in field_layouts.iter().enumerate() {
|
||||||
|
if *index == field_index as u64 {
|
||||||
|
let offset = i32::try_from(offset)
|
||||||
|
.expect("TODO gracefully handle usize -> i32 conversion in struct access");
|
||||||
|
|
||||||
|
let mem_flags = MemFlags::new();
|
||||||
|
let expr = build_expr(env, scope, module, builder, expr, procs);
|
||||||
|
|
||||||
|
let ret_type = layout_to_type(&field_layout, cfg.pointer_type());
|
||||||
|
|
||||||
|
return builder
|
||||||
|
.ins()
|
||||||
|
.load(ret_type, mem_flags, expr, Offset32::new(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += field_layout.stack_size(ptr_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("field access out of bounds: index {:?} in layouts {:?}", index, field_layouts)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Str(str_literal) => {
|
Str(str_literal) => {
|
||||||
if str_literal.is_empty() {
|
if str_literal.is_empty() {
|
||||||
panic!("TODO build an empty string in Crane");
|
panic!("TODO build an empty string in Crane");
|
||||||
} else {
|
} else {
|
||||||
let bytes_len = str_literal.len() + 1/* TODO drop the +1 when we have structs and this is no longer a NUL-terminated CString.*/;
|
let bytes_len = str_literal.len() + 1/* TODO drop the +1 when we have structs and this is no longer a NUL-terminated CString.*/;
|
||||||
let ptr = call_malloc(env, module, builder, bytes_len);
|
let size = builder.ins().iconst(types::I64, bytes_len as i64);
|
||||||
|
let ptr = call_malloc(env, module, builder, size);
|
||||||
let mem_flags = MemFlags::new();
|
let mem_flags = MemFlags::new();
|
||||||
|
|
||||||
// Copy the bytes from the string literal into the array
|
// Store the bytes from the string literal in the array
|
||||||
for (index, byte) in str_literal.bytes().enumerate() {
|
for (index, byte) in str_literal.bytes().enumerate() {
|
||||||
let val = builder.ins().iconst(types::I8, byte as i64);
|
let val = builder.ins().iconst(types::I8, byte as i64);
|
||||||
let offset = Offset32::new(index as i32);
|
let offset = Offset32::new(index as i32);
|
||||||
|
@ -294,7 +374,8 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
} else {
|
} else {
|
||||||
let elem_bytes = elem_layout.stack_size(ptr_bytes as u32);
|
let elem_bytes = elem_layout.stack_size(ptr_bytes as u32);
|
||||||
let bytes_len = elem_bytes as usize * elems.len();
|
let bytes_len = elem_bytes as usize * elems.len();
|
||||||
let elems_ptr = call_malloc(env, module, builder, bytes_len);
|
let size = builder.ins().iconst(types::I64, bytes_len as i64);
|
||||||
|
let elems_ptr = call_malloc(env, module, builder, size);
|
||||||
let mem_flags = MemFlags::new();
|
let mem_flags = MemFlags::new();
|
||||||
|
|
||||||
// Copy the elements from the literal into the array
|
// Copy the elements from the literal into the array
|
||||||
|
@ -308,18 +389,19 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
elems_ptr
|
elems_ptr
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store the pointer in slot 0
|
// Store the pointer
|
||||||
builder
|
{
|
||||||
.ins()
|
let offset = Offset32::new((Builtin::WRAPPER_PTR * ptr_bytes) as i32);
|
||||||
.stack_store(elems_ptr, slot, Offset32::new(0));
|
|
||||||
|
|
||||||
// Store the length in slot 1
|
builder.ins().stack_store(elems_ptr, slot, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the length
|
||||||
{
|
{
|
||||||
let length = builder.ins().iconst(env.ptr_sized_int(), elems.len() as i64);
|
let length = builder.ins().iconst(env.ptr_sized_int(), elems.len() as i64);
|
||||||
|
let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32);
|
||||||
|
|
||||||
builder
|
builder.ins().stack_store(length, slot, offset);
|
||||||
.ins()
|
|
||||||
.stack_store(length, slot, Offset32::new(ptr_bytes as i32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the pointer to the wrapper
|
// Return the pointer to the wrapper
|
||||||
|
@ -331,6 +413,21 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn layout_to_type<'a>(layout: &Layout<'a>, _pointer_type: Type) -> Type {
|
||||||
|
use roc_mono::layout::Builtin::*;
|
||||||
|
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(builtin) => match builtin {
|
||||||
|
Int64 => cranelift::prelude::types::I64,
|
||||||
|
Byte => cranelift::prelude::types::I8,
|
||||||
|
Bool => cranelift::prelude::types::B1,
|
||||||
|
Float64 => cranelift::prelude::types::F64,
|
||||||
|
other => panic!("I don't yet know how to make a type from {:?}", other),
|
||||||
|
},
|
||||||
|
other => panic!("I don't yet know how to make a type from {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Branch2<'a> {
|
struct Branch2<'a> {
|
||||||
cond: &'a Expr<'a>,
|
cond: &'a Expr<'a>,
|
||||||
cond_layout: &'a Layout<'a>,
|
cond_layout: &'a Layout<'a>,
|
||||||
|
@ -363,7 +460,7 @@ fn build_branch2<'a, B: Backend>(
|
||||||
let fail_block = builder.create_block();
|
let fail_block = builder.create_block();
|
||||||
|
|
||||||
match branch.cond_layout {
|
match branch.cond_layout {
|
||||||
Layout::Builtin(Builtin::Bool(_, _)) => {
|
Layout::Builtin(Builtin::Bool) => {
|
||||||
builder.ins().brnz(cond, pass_block, &[]);
|
builder.ins().brnz(cond, pass_block, &[]);
|
||||||
}
|
}
|
||||||
other => panic!("I don't know how to build a conditional for {:?}", other),
|
other => panic!("I don't know how to build a conditional for {:?}", other),
|
||||||
|
@ -645,14 +742,13 @@ fn call_by_name<'a, B: Backend>(
|
||||||
debug_assert!(args.len() == 1);
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
let list_ptr = build_arg(&args[0], env, scope, module, builder, procs);
|
let list_ptr = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
let ptr_bytes = env.cfg.pointer_bytes() as u32;
|
||||||
|
let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32);
|
||||||
|
|
||||||
// Get the usize int length
|
// Get the usize list length
|
||||||
builder.ins().load(
|
builder
|
||||||
env.ptr_sized_int(),
|
.ins()
|
||||||
MemFlags::new(),
|
.load(env.ptr_sized_int(), MemFlags::new(), list_ptr, offset)
|
||||||
list_ptr,
|
|
||||||
Offset32::new(env.cfg.pointer_bytes() as i32),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Symbol::INT_EQ_I64 | Symbol::INT_EQ_I8 | Symbol::INT_EQ_I1 => {
|
Symbol::INT_EQ_I64 | Symbol::INT_EQ_I8 | Symbol::INT_EQ_I1 => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
@ -674,68 +770,68 @@ fn call_by_name<'a, B: Backend>(
|
||||||
let wrapper_ptr = build_arg(&args[0], env, scope, module, builder, procs);
|
let wrapper_ptr = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
let elem_index = build_arg(&args[1], env, scope, module, builder, procs);
|
let elem_index = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
let elem_type = Type::int(64).unwrap(); // TODO Look this up instead of hardcoding it!
|
|
||||||
let elem_bytes = 8; // TODO Look this up instead of hardcoding it!
|
|
||||||
let elem_size = builder.ins().iconst(types::I64, elem_bytes);
|
|
||||||
|
|
||||||
// Load the pointer we got to the wrapper struct
|
|
||||||
let elems_ptr = builder.ins().load(
|
|
||||||
env.cfg.pointer_type(),
|
|
||||||
MemFlags::new(),
|
|
||||||
wrapper_ptr,
|
|
||||||
Offset32::new(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Multiply the requested index by the size of each element.
|
|
||||||
let offset = builder.ins().imul(elem_index, elem_size);
|
|
||||||
|
|
||||||
// Follow the pointer in the wrapper struct to the actual elements
|
|
||||||
builder.ins().load_complex(
|
|
||||||
elem_type,
|
|
||||||
MemFlags::new(),
|
|
||||||
&[elems_ptr, offset],
|
|
||||||
Offset32::new(0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Symbol::LIST_SET => {
|
|
||||||
let (_list_expr, list_layout) = &args[0];
|
let (_list_expr, list_layout) = &args[0];
|
||||||
|
|
||||||
|
// Get the usize list length
|
||||||
|
let ptr_bytes = env.cfg.pointer_bytes() as u32;
|
||||||
|
let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32);
|
||||||
|
let _list_len =
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.load(env.ptr_sized_int(), MemFlags::new(), wrapper_ptr, offset);
|
||||||
|
|
||||||
|
// TODO compare elem_index to _list_len to do array bounds checking.
|
||||||
|
|
||||||
match list_layout {
|
match list_layout {
|
||||||
Layout::Builtin(Builtin::List(elem_layout)) => {
|
Layout::Builtin(Builtin::List(elem_layout)) => {
|
||||||
// TODO try memcpy for shallow clones; it's probably faster
|
let cfg = env.cfg;
|
||||||
// let list_val = build_expr(env, scope, module, builder, list_expr, procs);
|
let elem_type = type_from_layout(cfg, elem_layout);
|
||||||
|
let elem_bytes = elem_layout.stack_size(cfg.pointer_bytes() as u32);
|
||||||
|
let elem_size = builder.ins().iconst(types::I64, elem_bytes as i64);
|
||||||
|
|
||||||
let num_elems = 10; // TODO FIXME read from List.len
|
|
||||||
let elem_bytes =
|
|
||||||
elem_layout.stack_size(env.cfg.pointer_bytes() as u32) as usize;
|
|
||||||
let bytes_len = (elem_bytes * num_elems) + 1/* TODO drop the +1 when we have structs and this is no longer NUL-terminated. */;
|
|
||||||
let wrapper_ptr = call_malloc(env, module, builder, bytes_len);
|
|
||||||
// let mem_flags = MemFlags::new();
|
|
||||||
|
|
||||||
// Copy the elements from the literal into the array
|
|
||||||
// for (index, elem) in elems.iter().enumerate() {
|
|
||||||
// let offset = Offset32::new(elem_bytes as i32 * index as i32);
|
|
||||||
// let val = build_expr(env, scope, module, builder, elem, procs);
|
|
||||||
|
|
||||||
// builder.ins().store(mem_flags, val, ptr, offset);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Add a NUL terminator at the end.
|
|
||||||
// TODO: Instead of NUL-terminating, return a struct
|
|
||||||
// with the pointer and also the length and capacity.
|
|
||||||
// let nul_terminator = builder.ins().iconst(types::I8, 0);
|
|
||||||
// let index = bytes_len as i32 - 1;
|
|
||||||
// let offset = Offset32::new(index);
|
|
||||||
|
|
||||||
// builder.ins().store(mem_flags, nul_terminator, ptr, offset);
|
|
||||||
// Load the pointer we got to the wrapper struct
|
// Load the pointer we got to the wrapper struct
|
||||||
let _elems_ptr = builder.ins().load(
|
let elems_ptr = builder.ins().load(
|
||||||
env.cfg.pointer_type(),
|
env.cfg.pointer_type(),
|
||||||
MemFlags::new(),
|
MemFlags::new(),
|
||||||
wrapper_ptr,
|
wrapper_ptr,
|
||||||
Offset32::new(0),
|
Offset32::new(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Multiply the requested index by the size of each element.
|
||||||
|
let offset = builder.ins().imul(elem_index, elem_size);
|
||||||
|
|
||||||
|
// Follow the pointer in the wrapper struct to the actual elements
|
||||||
|
builder.ins().load_complex(
|
||||||
|
elem_type,
|
||||||
|
MemFlags::new(),
|
||||||
|
&[elems_ptr, offset],
|
||||||
|
Offset32::new(0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("Invalid List layout for List.get: {:?}", list_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Symbol::LIST_SET => {
|
||||||
|
// set : List elem, Int, elem -> List elem
|
||||||
|
let wrapper_ptr = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
let (_list_expr, list_layout) = &args[0];
|
||||||
|
|
||||||
|
// Get the usize list length
|
||||||
|
let ptr_bytes = env.cfg.pointer_bytes() as u32;
|
||||||
|
let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32);
|
||||||
|
let _list_len =
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.load(env.ptr_sized_int(), MemFlags::new(), wrapper_ptr, offset);
|
||||||
|
|
||||||
|
// TODO do array bounds checking, and early return the original List if out of bounds
|
||||||
|
|
||||||
|
match list_layout {
|
||||||
|
Layout::Builtin(Builtin::List(elem_layout)) => {
|
||||||
|
let wrapper_ptr = clone_list(env, builder, module, wrapper_ptr, elem_layout);
|
||||||
|
|
||||||
list_set_in_place(
|
list_set_in_place(
|
||||||
env,
|
env,
|
||||||
wrapper_ptr,
|
wrapper_ptr,
|
||||||
|
@ -756,6 +852,17 @@ fn call_by_name<'a, B: Backend>(
|
||||||
// set : List elem, Int, elem -> List elem
|
// set : List elem, Int, elem -> List elem
|
||||||
debug_assert!(args.len() == 3);
|
debug_assert!(args.len() == 3);
|
||||||
|
|
||||||
|
// Get the usize list length
|
||||||
|
let wrapper_ptr = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
let ptr_bytes = env.cfg.pointer_bytes() as u32;
|
||||||
|
let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32);
|
||||||
|
let _list_len =
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.load(env.ptr_sized_int(), MemFlags::new(), wrapper_ptr, offset);
|
||||||
|
|
||||||
|
// TODO do array bounds checking, and early return the original List if out of bounds
|
||||||
|
|
||||||
let (list_expr, list_layout) = &args[0];
|
let (list_expr, list_layout) = &args[0];
|
||||||
let list_val = build_expr(env, scope, module, builder, list_expr, procs);
|
let list_val = build_expr(env, scope, module, builder, list_expr, procs);
|
||||||
|
|
||||||
|
@ -802,17 +909,13 @@ fn call_malloc<B: Backend>(
|
||||||
env: &Env<'_>,
|
env: &Env<'_>,
|
||||||
module: &mut Module<B>,
|
module: &mut Module<B>,
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
size: usize,
|
size: Value,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
// Declare malloc inside this function
|
// Declare malloc inside this function
|
||||||
let local_func = module.declare_func_in_func(env.malloc, &mut builder.func);
|
let local_func = module.declare_func_in_func(env.malloc, &mut builder.func);
|
||||||
|
|
||||||
// Convert the size argument to a Value
|
|
||||||
let ptr_size_type = module.target_config().pointer_type();
|
|
||||||
let size_arg = builder.ins().iconst(ptr_size_type, size as i64);
|
|
||||||
|
|
||||||
// Call malloc and return the resulting pointer
|
// Call malloc and return the resulting pointer
|
||||||
let call = builder.ins().call(local_func, &[size_arg]);
|
let call = builder.ins().call(local_func, &[size]);
|
||||||
let results = builder.inst_results(call);
|
let results = builder.inst_results(call);
|
||||||
|
|
||||||
debug_assert!(results.len() == 1);
|
debug_assert!(results.len() == 1);
|
||||||
|
@ -850,3 +953,77 @@ fn list_set_in_place<'a>(
|
||||||
|
|
||||||
wrapper_ptr
|
wrapper_ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_list<B: Backend>(
|
||||||
|
env: &Env<'_>,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
module: &mut Module<B>,
|
||||||
|
src_wrapper_ptr: Value,
|
||||||
|
elem_layout: &Layout<'_>,
|
||||||
|
) -> Value {
|
||||||
|
let cfg = env.cfg;
|
||||||
|
let ptr_bytes = env.cfg.pointer_bytes() as u32;
|
||||||
|
|
||||||
|
// Load the pointer we got to the wrapper struct
|
||||||
|
let elems_ptr = {
|
||||||
|
let offset = Offset32::new((Builtin::WRAPPER_PTR * ptr_bytes) as i32);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.load(cfg.pointer_type(), MemFlags::new(), src_wrapper_ptr, offset)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the usize list length
|
||||||
|
let list_len = {
|
||||||
|
let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32);
|
||||||
|
|
||||||
|
builder.ins().load(
|
||||||
|
env.ptr_sized_int(),
|
||||||
|
MemFlags::new(),
|
||||||
|
src_wrapper_ptr,
|
||||||
|
offset,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate the number of bytes we'll need to allocate.
|
||||||
|
let elem_bytes = builder.ins().iconst(
|
||||||
|
env.ptr_sized_int(),
|
||||||
|
elem_layout.stack_size(cfg.pointer_bytes() as u32) as i64,
|
||||||
|
);
|
||||||
|
let size = builder.ins().imul(elem_bytes, list_len);
|
||||||
|
|
||||||
|
// Allocate space for the new array that we'll copy into.
|
||||||
|
let new_elems_ptr = call_malloc(env, module, builder, size);
|
||||||
|
|
||||||
|
// Either memcpy or deep clone the array elements
|
||||||
|
if elem_layout.safe_to_memcpy() {
|
||||||
|
// Copy the bytes from the original array into the new
|
||||||
|
// one we just malloc'd.
|
||||||
|
//
|
||||||
|
// TODO how do we decide when to do the small memcpy vs the normal one?
|
||||||
|
builder.call_memcpy(env.cfg, new_elems_ptr, elems_ptr, size);
|
||||||
|
} else {
|
||||||
|
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a fresh wrapper struct for the newly populated array
|
||||||
|
let ptr_bytes = cfg.pointer_bytes() as u32;
|
||||||
|
let slot = builder.create_stack_slot(StackSlotData::new(
|
||||||
|
StackSlotKind::ExplicitSlot,
|
||||||
|
ptr_bytes * Builtin::LIST_WORDS,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Store the new pointer in slot 0 of the wrapper
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.stack_store(new_elems_ptr, slot, Offset32::new(0));
|
||||||
|
|
||||||
|
// Store the length in slot 1 of the wrapper
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.stack_store(list_len, slot, Offset32::new(ptr_bytes as i32));
|
||||||
|
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
|
||||||
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ pub fn type_from_layout(cfg: TargetFrontendConfig, layout: &Layout<'_>) -> Type
|
||||||
use roc_mono::layout::Layout::*;
|
use roc_mono::layout::Layout::*;
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
Pointer(_) | FunctionPointer(_, _) | Struct(_) | Tag(_) => cfg.pointer_type(),
|
FunctionPointer(_, _) | Struct(_) | Union(_) => cfg.pointer_type(),
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
Int64 => types::I64,
|
Int64 => types::I64,
|
||||||
Float64 => types::F64,
|
Float64 => types::F64,
|
||||||
Bool(_, _) => types::B1,
|
Bool => types::B1,
|
||||||
Byte(_) => types::I8,
|
Byte => types::I8,
|
||||||
Str | EmptyStr | Map(_, _) | EmptyMap | Set(_) | EmptySet | List(_) | EmptyList => {
|
Str | EmptyStr | Map(_, _) | EmptyMap | Set(_) | EmptySet | List(_) | EmptyList => {
|
||||||
cfg.pointer_type()
|
cfg.pointer_type()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ use bumpalo::Bump;
|
||||||
use inkwell::builder::Builder;
|
use inkwell::builder::Builder;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::module::{Linkage, Module};
|
use inkwell::module::{Linkage, Module};
|
||||||
use inkwell::types::BasicTypeEnum;
|
use inkwell::types::{BasicTypeEnum, IntType};
|
||||||
use inkwell::values::BasicValueEnum::{self, *};
|
use inkwell::values::BasicValueEnum::{self, *};
|
||||||
use inkwell::values::{FunctionValue, IntValue, PointerValue};
|
use inkwell::values::{FunctionValue, IntValue, PointerValue};
|
||||||
use inkwell::{AddressSpace, FloatPredicate, IntPredicate};
|
use inkwell::{AddressSpace, FloatPredicate, IntPredicate};
|
||||||
|
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
basic_type_from_layout, collection_wrapper, get_array_type, get_fn_type,
|
basic_type_from_layout, collection_wrapper, get_array_type, get_fn_type, ptr_int,
|
||||||
};
|
};
|
||||||
use roc_collections::all::ImMap;
|
use roc_collections::all::ImMap;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
|
@ -32,7 +32,13 @@ pub struct Env<'a, 'ctx, 'env> {
|
||||||
pub builder: &'env Builder<'ctx>,
|
pub builder: &'env Builder<'ctx>,
|
||||||
pub module: &'ctx Module<'ctx>,
|
pub module: &'ctx Module<'ctx>,
|
||||||
pub interns: Interns,
|
pub interns: Interns,
|
||||||
pub pointer_bytes: u32,
|
pub ptr_bytes: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
||||||
|
pub fn ptr_int(&self) -> IntType<'ctx> {
|
||||||
|
ptr_int(self.context, self.ptr_bytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_expr<'a, 'ctx, 'env>(
|
pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
|
@ -75,7 +81,8 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
ret_layout,
|
ret_layout,
|
||||||
cond_layout,
|
cond_layout,
|
||||||
} => {
|
} => {
|
||||||
let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout);
|
let ret_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, &ret_layout, env.ptr_bytes);
|
||||||
let switch_args = SwitchArgs {
|
let switch_args = SwitchArgs {
|
||||||
cond_layout: cond_layout.clone(),
|
cond_layout: cond_layout.clone(),
|
||||||
cond_expr: cond,
|
cond_expr: cond,
|
||||||
|
@ -92,7 +99,7 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
for (symbol, layout, expr) in stores.iter() {
|
for (symbol, layout, expr) in stores.iter() {
|
||||||
let val = build_expr(env, &scope, parent, &expr, procs);
|
let val = build_expr(env, &scope, parent, &expr, procs);
|
||||||
let expr_bt = basic_type_from_layout(env.arena, context, &layout);
|
let expr_bt = basic_type_from_layout(env.arena, context, &layout, env.ptr_bytes);
|
||||||
let alloca = create_entry_block_alloca(
|
let alloca = create_entry_block_alloca(
|
||||||
env,
|
env,
|
||||||
parent,
|
parent,
|
||||||
|
@ -122,14 +129,14 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
panic!("TODO create a phi node for &&");
|
panic!("TODO create a phi node for &&");
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut arg_vals: Vec<BasicValueEnum> =
|
let mut arg_tuples: Vec<(BasicValueEnum, &'a Layout<'a>)> =
|
||||||
Vec::with_capacity_in(args.len(), env.arena);
|
Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
for (arg, _layout) in args.iter() {
|
for (arg, layout) in args.iter() {
|
||||||
arg_vals.push(build_expr(env, scope, parent, arg, procs));
|
arg_tuples.push((build_expr(env, scope, parent, arg, procs), layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
call_with_args(*symbol, arg_vals.into_bump_slice(), env)
|
call_with_args(*symbol, arg_tuples.into_bump_slice(), env)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FunctionPointer(symbol) => {
|
FunctionPointer(symbol) => {
|
||||||
|
@ -165,7 +172,6 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
.left()
|
.left()
|
||||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||||
}
|
}
|
||||||
|
|
||||||
Load(symbol) => match scope.get(symbol) {
|
Load(symbol) => match scope.get(symbol) {
|
||||||
Some((_, ptr)) => env
|
Some((_, ptr)) => env
|
||||||
.builder
|
.builder
|
||||||
|
@ -209,13 +215,13 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
Array { elem_layout, elems } => {
|
Array { elem_layout, elems } => {
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout);
|
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
if elems.is_empty() {
|
if elems.is_empty() {
|
||||||
let array_type = get_array_type(&elem_type, 0);
|
let array_type = get_array_type(&elem_type, 0);
|
||||||
let ptr_type = array_type.ptr_type(AddressSpace::Generic);
|
let ptr_type = array_type.ptr_type(AddressSpace::Generic);
|
||||||
let struct_type = collection_wrapper(ctx, ptr_type);
|
let struct_type = collection_wrapper(ctx, ptr_type, env.ptr_bytes);
|
||||||
|
|
||||||
// The first field in the struct should be the pointer.
|
// The first field in the struct should be the pointer.
|
||||||
let struct_val = builder
|
let struct_val = builder
|
||||||
|
@ -230,11 +236,12 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||||
} else {
|
} else {
|
||||||
let len_u64 = elems.len() as u64;
|
let len_u64 = elems.len() as u64;
|
||||||
let elem_bytes = elem_layout.stack_size(env.pointer_bytes) as u64;
|
let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64;
|
||||||
|
|
||||||
let ptr = {
|
let ptr = {
|
||||||
let bytes_len = elem_bytes * len_u64;
|
let bytes_len = elem_bytes * len_u64;
|
||||||
let len = ctx.i32_type().const_int(bytes_len, false);
|
let len_type = env.ptr_int();
|
||||||
|
let len = len_type.const_int(bytes_len, false);
|
||||||
|
|
||||||
env.builder
|
env.builder
|
||||||
.build_array_malloc(elem_type, len, "create_list_ptr")
|
.build_array_malloc(elem_type, len, "create_list_ptr")
|
||||||
|
@ -252,8 +259,8 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let ptr_val = BasicValueEnum::PointerValue(ptr);
|
let ptr_val = BasicValueEnum::PointerValue(ptr);
|
||||||
let struct_type = collection_wrapper(ctx, ptr.get_type());
|
let struct_type = collection_wrapper(ctx, ptr.get_type(), env.ptr_bytes);
|
||||||
let len = BasicValueEnum::IntValue(ctx.i32_type().const_int(len_u64, false));
|
let len = BasicValueEnum::IntValue(env.ptr_int().const_int(len_u64, false));
|
||||||
let mut struct_val;
|
let mut struct_val;
|
||||||
|
|
||||||
// Field 0: pointer
|
// Field 0: pointer
|
||||||
|
@ -271,16 +278,6 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Field 2: capacity (initially set to length)
|
|
||||||
struct_val = builder
|
|
||||||
.build_insert_value(
|
|
||||||
struct_val,
|
|
||||||
len,
|
|
||||||
Builtin::WRAPPER_CAPACITY,
|
|
||||||
"insert_capacity",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,7 +293,8 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
for (field_expr, field_layout) in sorted_fields.iter() {
|
for (field_expr, field_layout) in sorted_fields.iter() {
|
||||||
let val = build_expr(env, &scope, parent, field_expr, procs);
|
let val = build_expr(env, &scope, parent, field_expr, procs);
|
||||||
let field_type = basic_type_from_layout(env.arena, env.context, &field_layout);
|
let field_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, &field_layout, env.ptr_bytes);
|
||||||
|
|
||||||
field_types.push(field_type);
|
field_types.push(field_type);
|
||||||
field_vals.push(val);
|
field_vals.push(val);
|
||||||
|
@ -315,6 +313,157 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||||
}
|
}
|
||||||
|
Tag {
|
||||||
|
union_size,
|
||||||
|
arguments,
|
||||||
|
..
|
||||||
|
} if *union_size == 1 => {
|
||||||
|
let it = arguments.iter();
|
||||||
|
|
||||||
|
let ctx = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Determine types
|
||||||
|
let num_fields = arguments.len() + 1;
|
||||||
|
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||||
|
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||||
|
|
||||||
|
for (field_expr, field_layout) in it {
|
||||||
|
let val = build_expr(env, &scope, parent, field_expr, procs);
|
||||||
|
let field_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, &field_layout, env.ptr_bytes);
|
||||||
|
|
||||||
|
field_types.push(field_type);
|
||||||
|
field_vals.push(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the struct_type
|
||||||
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
let mut struct_val = struct_type.const_zero().into();
|
||||||
|
|
||||||
|
// Insert field exprs into struct_val
|
||||||
|
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||||
|
struct_val = builder
|
||||||
|
.build_insert_value(struct_val, field_val, index as u32, "insert_field")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||||
|
}
|
||||||
|
Tag {
|
||||||
|
arguments,
|
||||||
|
tag_layout,
|
||||||
|
union_size,
|
||||||
|
tag_id,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let ptr_size = env.ptr_bytes;
|
||||||
|
|
||||||
|
let whole_size = tag_layout.stack_size(ptr_size);
|
||||||
|
let mut filler = tag_layout.stack_size(ptr_size);
|
||||||
|
|
||||||
|
let ctx = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Determine types
|
||||||
|
let num_fields = arguments.len() + 1;
|
||||||
|
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||||
|
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||||
|
|
||||||
|
// insert the discriminant value
|
||||||
|
if *union_size > 1 {
|
||||||
|
let val = env
|
||||||
|
.context
|
||||||
|
.i64_type()
|
||||||
|
.const_int(*tag_id as u64, true)
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let field_type = env.context.i64_type().into();
|
||||||
|
|
||||||
|
field_types.push(field_type);
|
||||||
|
field_vals.push(val);
|
||||||
|
|
||||||
|
let field_size = ptr_size;
|
||||||
|
filler -= field_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (field_expr, field_layout) in arguments.iter() {
|
||||||
|
let val = build_expr(env, &scope, parent, field_expr, procs);
|
||||||
|
let field_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, &field_layout, ptr_size);
|
||||||
|
|
||||||
|
field_types.push(field_type);
|
||||||
|
field_vals.push(val);
|
||||||
|
|
||||||
|
let field_size = field_layout.stack_size(ptr_size);
|
||||||
|
filler -= field_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO verify that this is required (better safe than sorry)
|
||||||
|
if filler > 0 {
|
||||||
|
field_types.push(env.context.i8_type().array_type(filler).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the struct_type
|
||||||
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
let mut struct_val = struct_type.const_zero().into();
|
||||||
|
|
||||||
|
// Insert field exprs into struct_val
|
||||||
|
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||||
|
struct_val = builder
|
||||||
|
.build_insert_value(struct_val, field_val, index as u32, "insert_field")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// How we create tag values
|
||||||
|
//
|
||||||
|
// The memory layout of tags can be different. e.g. in
|
||||||
|
//
|
||||||
|
// [ Ok Int, Err Str ]
|
||||||
|
//
|
||||||
|
// the `Ok` tag stores a 64-bit integer, the `Err` tag stores a struct.
|
||||||
|
// All tags of a union must have the same length, for easy addressing (e.g. array lookups).
|
||||||
|
// So we need to ask for the maximum of all tag's sizes, even if most tags won't use
|
||||||
|
// all that memory, and certainly won't use it in the same way (the tags have fields of
|
||||||
|
// different types/sizes)
|
||||||
|
//
|
||||||
|
// In llvm, we must be explicit about the type of value we're creating: we can't just
|
||||||
|
// make a unspecified block of memory. So what we do is create a byte array of the
|
||||||
|
// desired size. Then when we know which tag we have (which is here, in this function),
|
||||||
|
// we need to cast that down to the array of bytes that llvm expects
|
||||||
|
//
|
||||||
|
// There is the bitcast instruction, but it doesn't work for arrays. So we need to jump
|
||||||
|
// through some hoops using store and load to get this to work: the array is put into a
|
||||||
|
// one-element struct, which can be cast to the desired type.
|
||||||
|
//
|
||||||
|
// This tricks comes from
|
||||||
|
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
|
||||||
|
|
||||||
|
let array_type = ctx.i8_type().array_type(whole_size);
|
||||||
|
let struct_pointer = builder.build_alloca(array_type, "struct_poitner");
|
||||||
|
|
||||||
|
builder.build_store(
|
||||||
|
builder
|
||||||
|
.build_bitcast(
|
||||||
|
struct_pointer,
|
||||||
|
struct_type.ptr_type(inkwell::AddressSpace::Generic),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.into_pointer_value(),
|
||||||
|
struct_val,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = builder.build_load(struct_pointer, "");
|
||||||
|
|
||||||
|
// For unclear reasons, we can't cast an array to a struct on the other side.
|
||||||
|
// the solution is to wrap the array in a struct (yea...)
|
||||||
|
let wrapper_type = ctx.struct_type(&[array_type.into()], false);
|
||||||
|
let mut wrapper_val = wrapper_type.const_zero().into();
|
||||||
|
wrapper_val = builder
|
||||||
|
.build_insert_value(wrapper_val, result, 0, "insert_field")
|
||||||
|
.unwrap();
|
||||||
|
wrapper_val.into_struct_value().into()
|
||||||
|
}
|
||||||
Access {
|
Access {
|
||||||
label,
|
label,
|
||||||
field_layout,
|
field_layout,
|
||||||
|
@ -348,6 +497,73 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
.build_extract_value(struct_val, index, "field_access")
|
.build_extract_value(struct_val, index, "field_access")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
AccessAtIndex {
|
||||||
|
index,
|
||||||
|
expr,
|
||||||
|
is_unwrapped,
|
||||||
|
..
|
||||||
|
} if *is_unwrapped => {
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Get Struct val
|
||||||
|
// Since this is a one-element tag union, we get the correct struct immediately
|
||||||
|
let argument = build_expr(env, &scope, parent, expr, procs).into_struct_value();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.build_extract_value(
|
||||||
|
argument,
|
||||||
|
*index as u32,
|
||||||
|
env.arena.alloc(format!("tag_field_access_{}_", index)),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessAtIndex {
|
||||||
|
index,
|
||||||
|
expr,
|
||||||
|
field_layouts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Determine types, assumes the descriminant is in the field layouts
|
||||||
|
let num_fields = field_layouts.len();
|
||||||
|
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||||
|
let ptr_bytes = env.ptr_bytes;
|
||||||
|
|
||||||
|
for field_layout in field_layouts.iter() {
|
||||||
|
let field_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, &field_layout, ptr_bytes);
|
||||||
|
field_types.push(field_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the struct_type
|
||||||
|
let struct_type = env
|
||||||
|
.context
|
||||||
|
.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
|
||||||
|
// cast the argument bytes into the desired shape for this tag
|
||||||
|
let argument = build_expr(env, &scope, parent, expr, procs).into_struct_value();
|
||||||
|
let argument_pointer = builder.build_alloca(argument.get_type(), "");
|
||||||
|
builder.build_store(argument_pointer, argument);
|
||||||
|
|
||||||
|
let argument = builder
|
||||||
|
.build_load(
|
||||||
|
builder
|
||||||
|
.build_bitcast(
|
||||||
|
argument_pointer,
|
||||||
|
struct_type.ptr_type(inkwell::AddressSpace::Generic),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.into_pointer_value(),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.into_struct_value();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.build_extract_value(argument, *index as u32, "")
|
||||||
|
.expect("desired field did not decode")
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("I don't yet know how to LLVM build {:?}", expr);
|
panic!("I don't yet know how to LLVM build {:?}", expr);
|
||||||
}
|
}
|
||||||
|
@ -369,7 +585,7 @@ fn build_branch2<'a, 'ctx, 'env>(
|
||||||
procs: &Procs<'a>,
|
procs: &Procs<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let ret_layout = cond.ret_layout;
|
let ret_layout = cond.ret_layout;
|
||||||
let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout);
|
let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout, env.ptr_bytes);
|
||||||
|
|
||||||
let cond_expr = build_expr(env, scope, parent, cond.cond, procs);
|
let cond_expr = build_expr(env, scope, parent, cond.cond, procs);
|
||||||
|
|
||||||
|
@ -433,10 +649,8 @@ fn build_switch<'a, 'ctx, 'env>(
|
||||||
// they either need to all be i8, or i64
|
// they either need to all be i8, or i64
|
||||||
let int_val = match cond_layout {
|
let int_val = match cond_layout {
|
||||||
Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false),
|
Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false),
|
||||||
Layout::Builtin(Builtin::Bool(_, _)) => {
|
Layout::Builtin(Builtin::Bool) => context.bool_type().const_int(*int as u64, false),
|
||||||
context.bool_type().const_int(*int as u64, false)
|
Layout::Builtin(Builtin::Byte) => context.i8_type().const_int(*int as u64, false),
|
||||||
}
|
|
||||||
Layout::Builtin(Builtin::Byte(_)) => context.i8_type().const_int(*int as u64, false),
|
|
||||||
_ => panic!("Can't cast to cond_layout = {:?}", cond_layout),
|
_ => panic!("Can't cast to cond_layout = {:?}", cond_layout),
|
||||||
};
|
};
|
||||||
let block = context.append_basic_block(parent, format!("branch{}", int).as_str());
|
let block = context.append_basic_block(parent, format!("branch{}", int).as_str());
|
||||||
|
@ -566,12 +780,12 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
let args = proc.args;
|
let args = proc.args;
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let context = &env.context;
|
let context = &env.context;
|
||||||
let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout);
|
let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout, env.ptr_bytes);
|
||||||
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
||||||
let mut arg_symbols = Vec::new_in(arena);
|
let mut arg_symbols = Vec::new_in(arena);
|
||||||
|
|
||||||
for (layout, arg_symbol) in args.iter() {
|
for (layout, arg_symbol) in args.iter() {
|
||||||
let arg_type = basic_type_from_layout(arena, env.context, &layout);
|
let arg_type = basic_type_from_layout(arena, env.context, &layout, env.ptr_bytes);
|
||||||
|
|
||||||
arg_basic_types.push(arg_type);
|
arg_basic_types.push(arg_type);
|
||||||
arg_symbols.push(arg_symbol);
|
arg_symbols.push(arg_symbol);
|
||||||
|
@ -639,7 +853,7 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn call_with_args<'a, 'ctx, 'env>(
|
fn call_with_args<'a, 'ctx, 'env>(
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
args: &[BasicValueEnum<'ctx>],
|
args: &[(BasicValueEnum<'ctx>, &'a Layout<'a>)],
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
match symbol {
|
match symbol {
|
||||||
|
@ -647,8 +861,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_add(
|
let int_val = env.builder.build_int_add(
|
||||||
args[0].into_int_value(),
|
args[0].0.into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].0.into_int_value(),
|
||||||
"add_i64",
|
"add_i64",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -658,8 +872,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let float_val = env.builder.build_float_add(
|
let float_val = env.builder.build_float_add(
|
||||||
args[0].into_float_value(),
|
args[0].0.into_float_value(),
|
||||||
args[1].into_float_value(),
|
args[1].0.into_float_value(),
|
||||||
"add_f64",
|
"add_f64",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -669,8 +883,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_sub(
|
let int_val = env.builder.build_int_sub(
|
||||||
args[0].into_int_value(),
|
args[0].0.into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].0.into_int_value(),
|
||||||
"sub_I64",
|
"sub_I64",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -680,8 +894,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let float_val = env.builder.build_float_sub(
|
let float_val = env.builder.build_float_sub(
|
||||||
args[0].into_float_value(),
|
args[0].0.into_float_value(),
|
||||||
args[1].into_float_value(),
|
args[1].0.into_float_value(),
|
||||||
"sub_f64",
|
"sub_f64",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -691,8 +905,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_mul(
|
let int_val = env.builder.build_int_mul(
|
||||||
args[0].into_int_value(),
|
args[0].0.into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].0.into_int_value(),
|
||||||
"mul_i64",
|
"mul_i64",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -703,29 +917,26 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let int_val = env
|
let int_val = env
|
||||||
.builder
|
.builder
|
||||||
.build_int_neg(args[0].into_int_value(), "negate_i64");
|
.build_int_neg(args[0].0.into_int_value(), "negate_i64");
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
Symbol::LIST_LEN => {
|
Symbol::LIST_LEN => {
|
||||||
debug_assert!(args.len() == 1);
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
let wrapper_struct = args[0].into_struct_value();
|
let wrapper_struct = args[0].0.into_struct_value();
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
// Get the 32-bit int length
|
// Get the usize int length
|
||||||
let i32_val = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
|
builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value().into()
|
||||||
|
|
||||||
// cast the 32-bit length to a 64-bit int
|
|
||||||
BasicValueEnum::IntValue(builder.build_int_cast(i32_val, env.context.i64_type(), "i32_to_i64"))
|
|
||||||
}
|
}
|
||||||
Symbol::LIST_IS_EMPTY => {
|
Symbol::LIST_IS_EMPTY => {
|
||||||
debug_assert!(args.len() == 1);
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
let list_struct = args[0].into_struct_value();
|
let list_struct = args[0].0.into_struct_value();
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let list_len = builder.build_extract_value(list_struct, 1, "unwrapped_list_len").unwrap().into_int_value();
|
let list_len = builder.build_extract_value(list_struct, 1, "unwrapped_list_len").unwrap().into_int_value();
|
||||||
let zero = env.context.i32_type().const_zero();
|
let zero = env.ptr_int().const_zero();
|
||||||
let answer = builder.build_int_compare(IntPredicate::EQ, list_len, zero, "is_zero");
|
let answer = builder.build_int_compare(IntPredicate::EQ, list_len, zero, "is_zero");
|
||||||
|
|
||||||
BasicValueEnum::IntValue(answer)
|
BasicValueEnum::IntValue(answer)
|
||||||
|
@ -735,8 +946,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
let int_val = env.builder.build_int_compare(
|
||||||
IntPredicate::EQ,
|
IntPredicate::EQ,
|
||||||
args[0].into_int_value(),
|
args[0].0.into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].0.into_int_value(),
|
||||||
"cmp_i64",
|
"cmp_i64",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -747,8 +958,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
let int_val = env.builder.build_int_compare(
|
||||||
IntPredicate::EQ,
|
IntPredicate::EQ,
|
||||||
args[0].into_int_value(),
|
args[0].0.into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].0.into_int_value(),
|
||||||
"cmp_i1",
|
"cmp_i1",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -759,8 +970,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
let int_val = env.builder.build_int_compare(
|
||||||
IntPredicate::EQ,
|
IntPredicate::EQ,
|
||||||
args[0].into_int_value(),
|
args[0].0.into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].0.into_int_value(),
|
||||||
"cmp_i8",
|
"cmp_i8",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -771,8 +982,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let int_val = env.builder.build_float_compare(
|
let int_val = env.builder.build_float_compare(
|
||||||
FloatPredicate::OEQ,
|
FloatPredicate::OEQ,
|
||||||
args[0].into_float_value(),
|
args[0].0.into_float_value(),
|
||||||
args[1].into_float_value(),
|
args[1].0.into_float_value(),
|
||||||
"cmp_f64",
|
"cmp_f64",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -784,36 +995,45 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
// List.get : List elem, Int -> Result elem [ OutOfBounds ]*
|
// List.get : List elem, Int -> Result elem [ OutOfBounds ]*
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let wrapper_struct = args[0].into_struct_value();
|
let (_list_expr, list_layout) = &args[0];
|
||||||
let elem_index = args[1].into_int_value();
|
|
||||||
|
|
||||||
// Slot 1 in the wrapper struct is the length
|
let wrapper_struct = args[0].0.into_struct_value();
|
||||||
|
let elem_index = args[1].0.into_int_value();
|
||||||
|
|
||||||
|
// Get the length from the wrapper struct
|
||||||
let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
|
let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
|
||||||
|
|
||||||
// TODO here, check to see if the requested index exceeds the length of the array.
|
// TODO here, check to see if the requested index exceeds the length of the array.
|
||||||
|
|
||||||
// Slot 0 in the wrapper struct is the pointer to the array data
|
match list_layout {
|
||||||
let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value();
|
Layout::Builtin(Builtin::List(elem_layout)) => {
|
||||||
|
// Get the pointer to the array data
|
||||||
|
let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value();
|
||||||
|
|
||||||
let elem_bytes = 8; // TODO Look this size up instead of hardcoding it!
|
let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64;
|
||||||
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
|
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
|
||||||
|
|
||||||
// Calculate the offset at runtime by multiplying the index by the size of an element.
|
// Calculate the offset at runtime by multiplying the index by the size of an element.
|
||||||
let offset_bytes = builder.build_int_mul(elem_index, elem_size, "mul_offset");
|
let offset_bytes = builder.build_int_mul(elem_index, elem_size, "mul_offset");
|
||||||
|
|
||||||
// We already checked the bounds earlier.
|
// We already checked the bounds earlier.
|
||||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(array_data_ptr, &[offset_bytes], "elem") };
|
let elem_ptr = unsafe { builder.build_in_bounds_gep(array_data_ptr, &[offset_bytes], "elem") };
|
||||||
|
|
||||||
builder.build_load(elem_ptr, "List.get")
|
builder.build_load(elem_ptr, "List.get")
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("Invalid List layout for List.get: {:?}", list_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Symbol::LIST_SET /* TODO clone first for LIST_SET! */ | Symbol::LIST_SET_IN_PLACE => {
|
Symbol::LIST_SET /* TODO clone first for LIST_SET! */ | Symbol::LIST_SET_IN_PLACE => {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
debug_assert!(args.len() == 3);
|
debug_assert!(args.len() == 3);
|
||||||
|
|
||||||
let wrapper_struct = args[0].into_struct_value();
|
let wrapper_struct = args[0].0.into_struct_value();
|
||||||
let elem_index = args[1].into_int_value();
|
let elem_index = args[1].0.into_int_value();
|
||||||
let elem = args[2];
|
let (elem, elem_layout) = args[2];
|
||||||
|
|
||||||
// Slot 1 in the wrapper struct is the length
|
// Slot 1 in the wrapper struct is the length
|
||||||
let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
|
let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
|
||||||
|
@ -824,7 +1044,7 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
// Slot 0 in the wrapper struct is the pointer to the array data
|
// Slot 0 in the wrapper struct is the pointer to the array data
|
||||||
let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value();
|
let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value();
|
||||||
|
|
||||||
let elem_bytes = 8; // TODO Look this size up instead of hardcoding it!
|
let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64;
|
||||||
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
|
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
|
||||||
|
|
||||||
// Calculate the offset at runtime by multiplying the index by the size of an element.
|
// Calculate the offset at runtime by multiplying the index by the size of an element.
|
||||||
|
@ -845,7 +1065,13 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
.get_function(symbol.ident_string(&env.interns))
|
.get_function(symbol.ident_string(&env.interns))
|
||||||
.unwrap_or_else(|| panic!("Unrecognized function: {:?}", symbol));
|
.unwrap_or_else(|| panic!("Unrecognized function: {:?}", symbol));
|
||||||
|
|
||||||
let call = env.builder.build_call(fn_val, args, "tmp");
|
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
|
for (arg, _layout) in args.iter() {
|
||||||
|
arg_vals.push(*arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let call = env.builder.build_call(fn_val, arg_vals.into_bump_slice(), "call");
|
||||||
|
|
||||||
call.try_as_basic_value()
|
call.try_as_basic_value()
|
||||||
.left()
|
.left()
|
||||||
|
|
|
@ -2,7 +2,7 @@ use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::types::BasicTypeEnum::{self, *};
|
use inkwell::types::BasicTypeEnum::{self, *};
|
||||||
use inkwell::types::{ArrayType, BasicType, FunctionType, PointerType, StructType};
|
use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
|
|
||||||
use roc_mono::layout::Layout;
|
use roc_mono::layout::Layout;
|
||||||
|
@ -38,17 +38,20 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
context: &'ctx Context,
|
context: &'ctx Context,
|
||||||
layout: &Layout<'_>,
|
layout: &Layout<'_>,
|
||||||
|
ptr_bytes: u32,
|
||||||
) -> BasicTypeEnum<'ctx> {
|
) -> BasicTypeEnum<'ctx> {
|
||||||
use roc_mono::layout::Builtin::*;
|
use roc_mono::layout::Builtin::*;
|
||||||
use roc_mono::layout::Layout::*;
|
use roc_mono::layout::Layout::*;
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
FunctionPointer(args, ret_layout) => {
|
FunctionPointer(args, ret_layout) => {
|
||||||
let ret_type = basic_type_from_layout(arena, context, &ret_layout);
|
let ret_type = basic_type_from_layout(arena, context, &ret_layout, ptr_bytes);
|
||||||
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for arg_layout in args.iter() {
|
for arg_layout in args.iter() {
|
||||||
arg_basic_types.push(basic_type_from_layout(arena, context, arg_layout));
|
arg_basic_types.push(basic_type_from_layout(
|
||||||
|
arena, context, arg_layout, ptr_bytes,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_type = get_fn_type(&ret_type, arg_basic_types.into_bump_slice());
|
let fn_type = get_fn_type(&ret_type, arg_basic_types.into_bump_slice());
|
||||||
|
@ -61,24 +64,50 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
let mut field_types = Vec::with_capacity_in(sorted_fields.len(), arena);
|
let mut field_types = Vec::with_capacity_in(sorted_fields.len(), arena);
|
||||||
|
|
||||||
for (_, field_layout) in sorted_fields.iter() {
|
for (_, field_layout) in sorted_fields.iter() {
|
||||||
field_types.push(basic_type_from_layout(arena, context, field_layout));
|
field_types.push(basic_type_from_layout(
|
||||||
|
arena,
|
||||||
|
context,
|
||||||
|
field_layout,
|
||||||
|
ptr_bytes,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
context
|
context
|
||||||
.struct_type(field_types.into_bump_slice(), false)
|
.struct_type(field_types.into_bump_slice(), false)
|
||||||
.as_basic_type_enum()
|
.as_basic_type_enum()
|
||||||
}
|
}
|
||||||
Tag(_fields) => {
|
Union(tags) if tags.len() == 1 => {
|
||||||
panic!("TODO layout_to_basic_type for Tag");
|
let layouts = tags.iter().next().unwrap();
|
||||||
|
|
||||||
|
// Determine types
|
||||||
|
let mut field_types = Vec::with_capacity_in(layouts.len(), arena);
|
||||||
|
|
||||||
|
for layout in layouts.iter() {
|
||||||
|
field_types.push(basic_type_from_layout(arena, context, layout, ptr_bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
context
|
||||||
|
.struct_type(field_types.into_bump_slice(), false)
|
||||||
|
.as_basic_type_enum()
|
||||||
}
|
}
|
||||||
Pointer(_layout) => {
|
Union(_) => {
|
||||||
panic!("TODO layout_to_basic_type for Pointer");
|
// TODO make this dynamic
|
||||||
|
let ptr_size = std::mem::size_of::<i64>();
|
||||||
|
let union_size = layout.stack_size(ptr_size as u32);
|
||||||
|
|
||||||
|
let array_type = context
|
||||||
|
.i8_type()
|
||||||
|
.array_type(union_size)
|
||||||
|
.as_basic_type_enum();
|
||||||
|
|
||||||
|
context.struct_type(&[array_type], false).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
Int64 => context.i64_type().as_basic_type_enum(),
|
Int64 => context.i64_type().as_basic_type_enum(),
|
||||||
Float64 => context.f64_type().as_basic_type_enum(),
|
Float64 => context.f64_type().as_basic_type_enum(),
|
||||||
Bool(_, _) => context.bool_type().as_basic_type_enum(),
|
Bool => context.bool_type().as_basic_type_enum(),
|
||||||
Byte(_) => context.i8_type().as_basic_type_enum(),
|
Byte => context.i8_type().as_basic_type_enum(),
|
||||||
Str | EmptyStr => context
|
Str | EmptyStr => context
|
||||||
.i8_type()
|
.i8_type()
|
||||||
.ptr_type(AddressSpace::Generic)
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
@ -86,17 +115,17 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
Map(_, _) | EmptyMap => panic!("TODO layout_to_basic_type for Builtin::Map"),
|
Map(_, _) | EmptyMap => panic!("TODO layout_to_basic_type for Builtin::Map"),
|
||||||
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"),
|
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"),
|
||||||
List(elem_layout) => {
|
List(elem_layout) => {
|
||||||
let ptr_type = basic_type_from_layout(arena, context, elem_layout)
|
let ptr_type = basic_type_from_layout(arena, context, elem_layout, ptr_bytes)
|
||||||
.ptr_type(AddressSpace::Generic);
|
.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
collection_wrapper(context, ptr_type).into()
|
collection_wrapper(context, ptr_type, ptr_bytes).into()
|
||||||
}
|
}
|
||||||
EmptyList => {
|
EmptyList => {
|
||||||
let array_type =
|
let array_type =
|
||||||
get_array_type(&context.opaque_struct_type("empty_list_elem").into(), 0);
|
get_array_type(&context.opaque_struct_type("empty_list_elem").into(), 0);
|
||||||
let ptr_type = array_type.ptr_type(AddressSpace::Generic);
|
let ptr_type = array_type.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
collection_wrapper(context, ptr_type).into()
|
collection_wrapper(context, ptr_type, ptr_bytes).into()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -106,9 +135,24 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
pub fn collection_wrapper<'ctx>(
|
pub fn collection_wrapper<'ctx>(
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
ptr_type: PointerType<'ctx>,
|
ptr_type: PointerType<'ctx>,
|
||||||
|
ptr_bytes: u32,
|
||||||
) -> StructType<'ctx> {
|
) -> StructType<'ctx> {
|
||||||
let ptr_type_enum = BasicTypeEnum::PointerType(ptr_type);
|
let ptr_type_enum = BasicTypeEnum::PointerType(ptr_type);
|
||||||
let u32_type = BasicTypeEnum::IntType(ctx.i32_type());
|
let len_type = BasicTypeEnum::IntType(ptr_int(ctx, ptr_bytes));
|
||||||
|
|
||||||
ctx.struct_type(&[ptr_type_enum, u32_type, u32_type], false)
|
ctx.struct_type(&[ptr_type_enum, len_type], false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ptr_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
|
||||||
|
match ptr_bytes {
|
||||||
|
1 => ctx.i8_type(),
|
||||||
|
2 => ctx.i16_type(),
|
||||||
|
4 => ctx.i32_type(),
|
||||||
|
8 => ctx.i64_type(),
|
||||||
|
16 => ctx.i128_type(),
|
||||||
|
_ => panic!(
|
||||||
|
"Invalid target: Roc does't support compiling to {}-bit systems.",
|
||||||
|
ptr_bytes * 8
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ mod test_gen {
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||||
let mono_expr = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
let mono_expr = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns
|
// Put this module's ident_ids back in the interns
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ mod test_gen {
|
||||||
builder.finalize();
|
builder.finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.define_function(main_fn, &mut ctx).expect("declare main");
|
module.define_function(main_fn, &mut ctx).expect("crane declare main");
|
||||||
module.clear_context(&mut ctx);
|
module.clear_context(&mut ctx);
|
||||||
|
|
||||||
// Perform linking
|
// Perform linking
|
||||||
|
@ -200,16 +201,16 @@ mod test_gen {
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_fn_type = basic_type_from_layout(&arena, &context, &layout)
|
|
||||||
.fn_type(&[], false);
|
|
||||||
let main_fn_name = "$Test.main";
|
|
||||||
|
|
||||||
let execution_engine =
|
let execution_engine =
|
||||||
module
|
module
|
||||||
.create_jit_execution_engine(OptimizationLevel::None)
|
.create_jit_execution_engine(OptimizationLevel::None)
|
||||||
.expect("Error creating JIT execution engine for test");
|
.expect("Error creating JIT execution engine for test");
|
||||||
|
|
||||||
let pointer_bytes = execution_engine.get_target_data().get_pointer_byte_size(None);
|
let ptr_bytes = execution_engine.get_target_data().get_pointer_byte_size(None);
|
||||||
|
|
||||||
|
let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes)
|
||||||
|
.fn_type(&[], false);
|
||||||
|
let main_fn_name = "$Test.main";
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut env = roc_gen::llvm::build::Env {
|
let mut env = roc_gen::llvm::build::Env {
|
||||||
|
@ -218,7 +219,7 @@ mod test_gen {
|
||||||
context: &context,
|
context: &context,
|
||||||
interns,
|
interns,
|
||||||
module: arena.alloc(module),
|
module: arena.alloc(module),
|
||||||
pointer_bytes
|
ptr_bytes
|
||||||
};
|
};
|
||||||
let mut procs = Procs::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
@ -226,6 +227,8 @@ mod test_gen {
|
||||||
// Populate Procs and get the low-level Expr from the canonical Expr
|
// Populate Procs and get the low-level Expr from the canonical Expr
|
||||||
let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
|
dbg!(&main_body);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
|
||||||
|
@ -335,16 +338,16 @@ mod test_gen {
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_fn_type = basic_type_from_layout(&arena, &context, &layout)
|
|
||||||
.fn_type(&[], false);
|
|
||||||
let main_fn_name = "$Test.main";
|
|
||||||
|
|
||||||
let execution_engine =
|
let execution_engine =
|
||||||
module
|
module
|
||||||
.create_jit_execution_engine(OptimizationLevel::None)
|
.create_jit_execution_engine(OptimizationLevel::None)
|
||||||
.expect("Error creating JIT execution engine for test");
|
.expect("Error creating JIT execution engine for test");
|
||||||
|
|
||||||
let pointer_bytes = execution_engine.get_target_data().get_pointer_byte_size(None);
|
let ptr_bytes = execution_engine.get_target_data().get_pointer_byte_size(None);
|
||||||
|
let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes)
|
||||||
|
.fn_type(&[], false);
|
||||||
|
let main_fn_name = "$Test.main";
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut env = roc_gen::llvm::build::Env {
|
let mut env = roc_gen::llvm::build::Env {
|
||||||
|
@ -353,7 +356,7 @@ mod test_gen {
|
||||||
context: &context,
|
context: &context,
|
||||||
interns,
|
interns,
|
||||||
module: arena.alloc(module),
|
module: arena.alloc(module),
|
||||||
pointer_bytes
|
ptr_bytes
|
||||||
};
|
};
|
||||||
let mut procs = Procs::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
@ -550,7 +553,26 @@ mod test_gen {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_shared_int_list() {
|
fn set_shared_int_list() {
|
||||||
|
assert_crane_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
shared = [ 2, 4 ]
|
||||||
|
|
||||||
|
# This should not mutate the original
|
||||||
|
x = List.set shared 1 77
|
||||||
|
|
||||||
|
List.getUnsafe shared 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
4,
|
||||||
|
i64,
|
||||||
|
|x| x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_unique_int_list() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -594,6 +616,23 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// doesn't work yet. The condition must be cast to an integer to use a jump table
|
||||||
|
// #[test]
|
||||||
|
// fn branch_third_float() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// when 10.0 is
|
||||||
|
// 1.0 -> 63
|
||||||
|
// 2 -> 48
|
||||||
|
// _ -> 112
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 112.0,
|
||||||
|
// f64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn branch_first_int() {
|
fn branch_first_int() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -624,6 +663,85 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn branch_third_int() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 10 is
|
||||||
|
1 -> 63
|
||||||
|
2 -> 48
|
||||||
|
_ -> 112
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
112,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn branch_store_variable() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 0 is
|
||||||
|
1 -> 12
|
||||||
|
a -> a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_element_tag() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : [ Pair Int ]
|
||||||
|
x = Pair 2
|
||||||
|
|
||||||
|
0x3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_one_element_tag() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : [ Pair Int Int ]
|
||||||
|
x = Pair 0x2 0x3
|
||||||
|
|
||||||
|
when x is
|
||||||
|
Pair l r -> l + r
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
5,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn twice_record_access() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x = {a: 0x2, b: 0x3 }
|
||||||
|
|
||||||
|
x.a + x.b
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
5,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_when_one_branch() {
|
fn gen_when_one_branch() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -1065,6 +1183,136 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn applied_tag_nothing() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Maybe a : [ Just a, Nothing ]
|
||||||
|
|
||||||
|
x : Maybe Int
|
||||||
|
x = Nothing
|
||||||
|
|
||||||
|
0x1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn applied_tag_just() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Maybe a : [ Just a, Nothing ]
|
||||||
|
|
||||||
|
y : Maybe Int
|
||||||
|
y = Just 0x4
|
||||||
|
|
||||||
|
0x1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn applied_tag_just_unit() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// Fruit : [ Orange, Apple, Banana ]
|
||||||
|
// Maybe a : [ Just a, Nothing ]
|
||||||
|
//
|
||||||
|
// orange : Fruit
|
||||||
|
// orange = Orange
|
||||||
|
//
|
||||||
|
// y : Maybe Fruit
|
||||||
|
// y = Just orange
|
||||||
|
//
|
||||||
|
// 0x1
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 1,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_on_nothing() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : [ Nothing, Just Int ]
|
||||||
|
x = Nothing
|
||||||
|
|
||||||
|
when x is
|
||||||
|
Nothing -> 0x2
|
||||||
|
Just _ -> 0x1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
2,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn when_on_just() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// x : [ Nothing, Just Int ]
|
||||||
|
// x = Just 41
|
||||||
|
//
|
||||||
|
// case x of
|
||||||
|
// Just v -> v + 0x1
|
||||||
|
// Nothing -> 0x1
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 42,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_on_result() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : Result Int Int
|
||||||
|
x = Err 41
|
||||||
|
|
||||||
|
when x is
|
||||||
|
Err v -> v + 1
|
||||||
|
Ok _ -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
42,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_on_these() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : [ This Int, These Int Int ]
|
||||||
|
x = These 0x3 0x2
|
||||||
|
|
||||||
|
when x is
|
||||||
|
These a b -> a + b
|
||||||
|
This v -> v
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
5,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_record() {
|
fn basic_record() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -1174,5 +1422,17 @@ mod test_gen {
|
||||||
19,
|
19,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
rec = { x: 15, y: 17, z: 19 }
|
||||||
|
|
||||||
|
rec.z + rec.x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
34,
|
||||||
|
i64
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1137
compiler/mono/src/decision_tree.rs
Normal file
1137
compiler/mono/src/decision_tree.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,5 @@
|
||||||
use crate::layout::{Builtin, Layout};
|
use crate::layout::{Builtin, Layout};
|
||||||
|
use crate::pattern::Ctor;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can;
|
use roc_can;
|
||||||
|
@ -7,6 +8,7 @@ use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::{Content, ContentHash, FlatType, Subs, Variable};
|
use roc_types::subs::{Content, ContentHash, FlatType, Subs, Variable};
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Default)]
|
#[derive(Clone, Debug, PartialEq, Default)]
|
||||||
pub struct Procs<'a> {
|
pub struct Procs<'a> {
|
||||||
|
@ -97,13 +99,14 @@ pub struct Proc<'a> {
|
||||||
pub ret_layout: Layout<'a>,
|
pub ret_layout: Layout<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Env<'a, 'i> {
|
pub struct Env<'a, 'i> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
pub subs: &'a mut Subs,
|
pub subs: &'a mut Subs,
|
||||||
pub home: ModuleId,
|
pub home: ModuleId,
|
||||||
pub ident_ids: &'i mut IdentIds,
|
pub ident_ids: &'i mut IdentIds,
|
||||||
pub pointer_size: u32,
|
pub pointer_size: u32,
|
||||||
symbol_counter: usize,
|
symbol_counter: usize,
|
||||||
|
pub jump_counter: &'a mut u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> Env<'a, 'i> {
|
impl<'a, 'i> Env<'a, 'i> {
|
||||||
|
@ -181,8 +184,10 @@ pub enum Expr<'a> {
|
||||||
},
|
},
|
||||||
Tag {
|
Tag {
|
||||||
tag_layout: Layout<'a>,
|
tag_layout: Layout<'a>,
|
||||||
name: TagName,
|
tag_name: TagName,
|
||||||
arguments: &'a [Expr<'a>],
|
tag_id: u8,
|
||||||
|
union_size: u8,
|
||||||
|
arguments: &'a [(Expr<'a>, Layout<'a>)],
|
||||||
},
|
},
|
||||||
Struct(&'a [(Expr<'a>, Layout<'a>)]),
|
Struct(&'a [(Expr<'a>, Layout<'a>)]),
|
||||||
Access {
|
Access {
|
||||||
|
@ -191,12 +196,21 @@ pub enum Expr<'a> {
|
||||||
struct_layout: Layout<'a>,
|
struct_layout: Layout<'a>,
|
||||||
record: &'a Expr<'a>,
|
record: &'a Expr<'a>,
|
||||||
},
|
},
|
||||||
|
AccessAtIndex {
|
||||||
|
index: u64,
|
||||||
|
field_layouts: &'a [Layout<'a>],
|
||||||
|
expr: &'a Expr<'a>,
|
||||||
|
is_unwrapped: bool,
|
||||||
|
},
|
||||||
|
|
||||||
Array {
|
Array {
|
||||||
elem_layout: Layout<'a>,
|
elem_layout: Layout<'a>,
|
||||||
elems: &'a [Expr<'a>],
|
elems: &'a [Expr<'a>],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Label(u64, &'a Expr<'a>),
|
||||||
|
Jump(u64),
|
||||||
|
|
||||||
RuntimeError(&'a str),
|
RuntimeError(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,6 +231,7 @@ impl<'a> Expr<'a> {
|
||||||
ident_ids,
|
ident_ids,
|
||||||
pointer_size,
|
pointer_size,
|
||||||
symbol_counter: 0,
|
symbol_counter: 0,
|
||||||
|
jump_counter: arena.alloc(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
from_can(&mut env, can_expr, procs, None)
|
from_can(&mut env, can_expr, procs, None)
|
||||||
|
@ -359,6 +374,7 @@ fn pattern_to_when<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn from_can<'a>(
|
fn from_can<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
|
@ -412,7 +428,7 @@ fn from_can<'a>(
|
||||||
store_pattern(
|
store_pattern(
|
||||||
env,
|
env,
|
||||||
mono_pattern,
|
mono_pattern,
|
||||||
loc_expr.value,
|
&loc_expr.value,
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
procs,
|
procs,
|
||||||
&mut stored,
|
&mut stored,
|
||||||
|
@ -503,8 +519,8 @@ fn from_can<'a>(
|
||||||
Ok(Layout::Builtin(builtin)) => match builtin {
|
Ok(Layout::Builtin(builtin)) => match builtin {
|
||||||
Builtin::Int64 => Symbol::INT_EQ_I64,
|
Builtin::Int64 => Symbol::INT_EQ_I64,
|
||||||
Builtin::Float64 => Symbol::FLOAT_EQ,
|
Builtin::Float64 => Symbol::FLOAT_EQ,
|
||||||
Builtin::Bool(_, _) => Symbol::INT_EQ_I1,
|
Builtin::Bool => Symbol::INT_EQ_I1,
|
||||||
Builtin::Byte(_) => Symbol::INT_EQ_I8,
|
Builtin::Byte => Symbol::INT_EQ_I8,
|
||||||
_ => panic!("Equality not implemented for {:?}", builtin),
|
_ => panic!("Equality not implemented for {:?}", builtin),
|
||||||
},
|
},
|
||||||
Ok(complex) => panic!(
|
Ok(complex) => panic!(
|
||||||
|
@ -572,6 +588,10 @@ fn from_can<'a>(
|
||||||
// It might even be the anonymous result of a conditional:
|
// It might even be the anonymous result of a conditional:
|
||||||
//
|
//
|
||||||
// ((if x > 0 then \a -> a else \_ -> 0) 5)
|
// ((if x > 0 then \a -> a else \_ -> 0) 5)
|
||||||
|
//
|
||||||
|
// It could be named too:
|
||||||
|
//
|
||||||
|
// ((if x > 0 then foo else bar) 5)
|
||||||
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
||||||
|
|
||||||
for (_, loc_arg) in loc_args {
|
for (_, loc_arg) in loc_args {
|
||||||
|
@ -654,28 +674,68 @@ fn from_can<'a>(
|
||||||
|
|
||||||
Tag {
|
Tag {
|
||||||
variant_var,
|
variant_var,
|
||||||
name,
|
name: tag_name,
|
||||||
arguments: args,
|
arguments: args,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
|
let mut fields = std::vec::Vec::new();
|
||||||
|
|
||||||
|
match roc_types::pretty_print::chase_ext_tag_union(env.subs, variant_var, &mut fields) {
|
||||||
|
Ok(()) | Err((_, Content::FlexVar(_))) => {}
|
||||||
|
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.sort();
|
||||||
|
let tag_id = fields
|
||||||
|
.iter()
|
||||||
|
.position(|(key, _)| key == &tag_name)
|
||||||
|
.expect("tag must be in its own type");
|
||||||
|
|
||||||
match Layout::from_var(arena, variant_var, &env.subs, env.pointer_size) {
|
match Layout::from_var(arena, variant_var, &env.subs, env.pointer_size) {
|
||||||
Ok(Layout::Builtin(Builtin::Bool(_smaller, larger))) => Expr::Bool(name == larger),
|
Ok(Layout::Builtin(Builtin::Bool)) => Expr::Bool(tag_id != 0),
|
||||||
Ok(Layout::Builtin(Builtin::Byte(tags))) => match tags.get(&name) {
|
Ok(Layout::Builtin(Builtin::Byte)) => Expr::Byte(tag_id as u8),
|
||||||
Some(v) => Expr::Byte(*v),
|
|
||||||
None => panic!("Tag name is not part of the type"),
|
|
||||||
},
|
|
||||||
Ok(layout) => {
|
Ok(layout) => {
|
||||||
let mut arguments = Vec::with_capacity_in(args.len(), arena);
|
let mut arguments = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for (_, arg) in args {
|
for (arg_var, arg) in args {
|
||||||
arguments.push(from_can(env, arg.value, procs, None));
|
let arg_layout =
|
||||||
|
Layout::from_var(env.arena, arg_var, env.subs, env.pointer_size)
|
||||||
|
.expect("invalid ret_layout");
|
||||||
|
|
||||||
|
arguments.push((from_can(env, arg.value, procs, None), arg_layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut tags = std::vec::Vec::new();
|
||||||
|
match roc_types::pretty_print::chase_ext_tag_union(
|
||||||
|
env.subs,
|
||||||
|
variant_var,
|
||||||
|
&mut tags,
|
||||||
|
) {
|
||||||
|
Ok(()) => {
|
||||||
|
tags.sort();
|
||||||
|
}
|
||||||
|
other => panic!("invalid value in ext_var {:?}", other),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut opt_tag_id = None;
|
||||||
|
for (index, (name, _)) in tags.iter().enumerate() {
|
||||||
|
if name == &tag_name {
|
||||||
|
opt_tag_id = Some(index as u8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let union_size = tags.len() as u8;
|
||||||
|
|
||||||
|
let tag_id = opt_tag_id.expect("Tag must be in its own type");
|
||||||
|
|
||||||
Expr::Tag {
|
Expr::Tag {
|
||||||
tag_layout: layout,
|
tag_layout: layout,
|
||||||
name,
|
tag_name,
|
||||||
|
tag_id,
|
||||||
|
union_size,
|
||||||
arguments: arguments.into_bump_slice(),
|
arguments: arguments.into_bump_slice(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -760,7 +820,7 @@ fn from_can<'a>(
|
||||||
fn store_pattern<'a>(
|
fn store_pattern<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_pat: Pattern,
|
can_pat: Pattern,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: &roc_can::expr::Expr,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
stored: &mut Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>,
|
stored: &mut Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>,
|
||||||
|
@ -788,13 +848,15 @@ fn store_pattern<'a>(
|
||||||
// identity 5
|
// identity 5
|
||||||
//
|
//
|
||||||
match can_pat {
|
match can_pat {
|
||||||
Identifier(symbol) => stored.push((symbol, layout, from_can(env, can_expr, procs, None))),
|
Identifier(symbol) => {
|
||||||
|
stored.push((symbol, layout, from_can(env, can_expr.clone(), procs, None)))
|
||||||
|
}
|
||||||
Underscore => {
|
Underscore => {
|
||||||
// Since _ is never read, it's safe to reassign it.
|
// Since _ is never read, it's safe to reassign it.
|
||||||
stored.push((
|
stored.push((
|
||||||
Symbol::UNDERSCORE,
|
Symbol::UNDERSCORE,
|
||||||
layout,
|
layout,
|
||||||
from_can(env, can_expr, procs, None),
|
from_can(env, can_expr.clone(), procs, None),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -803,6 +865,96 @@ fn store_pattern<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn store_pattern2<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
can_pat: &Pattern<'a>,
|
||||||
|
outer_symbol: Symbol,
|
||||||
|
_index: usize,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
stored: &mut Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
use Pattern::*;
|
||||||
|
|
||||||
|
match can_pat {
|
||||||
|
Identifier(symbol) => {
|
||||||
|
if true {
|
||||||
|
// stored.push((*symbol, layout, Expr::Load(outer_symbol)))
|
||||||
|
// let load = Expr::AccessAtIndex {
|
||||||
|
// index: index as u64,
|
||||||
|
// field_layouts: env.arena.alloc([
|
||||||
|
// Layout::Builtin(Builtin::Int64),
|
||||||
|
// Layout::Builtin(Builtin::Int64),
|
||||||
|
// ]),
|
||||||
|
// expr: env.arena.alloc(Expr::Load(outer_symbol)),
|
||||||
|
// };
|
||||||
|
|
||||||
|
let load = Expr::Load(outer_symbol);
|
||||||
|
stored.push((*symbol, layout, load))
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Underscore => {
|
||||||
|
// Since _ is never read, it's safe to reassign it.
|
||||||
|
stored.push((Symbol::UNDERSCORE, layout, Expr::Load(outer_symbol)))
|
||||||
|
}
|
||||||
|
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
||||||
|
AppliedTag {
|
||||||
|
union, arguments, ..
|
||||||
|
} => {
|
||||||
|
let is_unwrapped = union.alternatives.len() == 1;
|
||||||
|
|
||||||
|
let mut arg_layouts = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
|
|
||||||
|
if !is_unwrapped {
|
||||||
|
// add an element for the tag discriminant
|
||||||
|
arg_layouts.push(Layout::Builtin(Builtin::Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, layout) in arguments {
|
||||||
|
arg_layouts.push(layout.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, (argument, arg_layout)) in arguments.iter().enumerate() {
|
||||||
|
let load = Expr::AccessAtIndex {
|
||||||
|
is_unwrapped,
|
||||||
|
index: (!is_unwrapped as usize + index) as u64,
|
||||||
|
field_layouts: arg_layouts.clone().into_bump_slice(),
|
||||||
|
expr: env.arena.alloc(Expr::Load(outer_symbol)),
|
||||||
|
};
|
||||||
|
match argument {
|
||||||
|
Identifier(symbol) => {
|
||||||
|
// store immediately in the given symbol
|
||||||
|
stored.push((*symbol, arg_layout.clone(), load));
|
||||||
|
}
|
||||||
|
Underscore => {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// store the field in a symbol, and continue matching on it
|
||||||
|
let symbol = env.fresh_symbol();
|
||||||
|
stored.push((symbol, layout.clone(), load));
|
||||||
|
|
||||||
|
store_pattern2(env, argument, symbol, 0, arg_layout.clone(), stored)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Shadowed(region, ident) => {
|
||||||
|
return Err(format!(
|
||||||
|
"The pattern at {:?} shadows variable {:?}",
|
||||||
|
region, ident
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("TODO store_pattern for {:?}", can_pat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn from_can_when<'a>(
|
fn from_can_when<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
cond_var: Variable,
|
cond_var: Variable,
|
||||||
|
@ -814,8 +966,6 @@ fn from_can_when<'a>(
|
||||||
)>,
|
)>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
) -> Expr<'a> {
|
) -> Expr<'a> {
|
||||||
use Pattern::*;
|
|
||||||
|
|
||||||
match branches.len() {
|
match branches.len() {
|
||||||
0 => {
|
0 => {
|
||||||
// A when-expression with no branches is a runtime error.
|
// A when-expression with no branches is a runtime error.
|
||||||
|
@ -830,241 +980,83 @@ fn from_can_when<'a>(
|
||||||
let (loc_when_pattern, loc_branch) = branches.into_iter().next().unwrap();
|
let (loc_when_pattern, loc_branch) = branches.into_iter().next().unwrap();
|
||||||
|
|
||||||
let mono_pattern = from_can_pattern(env, &loc_when_pattern.value);
|
let mono_pattern = from_can_pattern(env, &loc_when_pattern.value);
|
||||||
store_pattern(
|
|
||||||
env,
|
|
||||||
mono_pattern,
|
|
||||||
loc_cond.value,
|
|
||||||
cond_var,
|
|
||||||
procs,
|
|
||||||
&mut stored,
|
|
||||||
);
|
|
||||||
|
|
||||||
let ret = from_can(env, loc_branch.value, procs, None);
|
let cond_layout = Layout::from_var(env.arena, cond_var, env.subs, env.pointer_size)
|
||||||
|
.unwrap_or_else(|err| panic!("TODO turn this into a RuntimeError {:?}", err));
|
||||||
|
let cond_symbol = env.fresh_symbol();
|
||||||
|
let cond = from_can(env, loc_cond.value, procs, None);
|
||||||
|
stored.push((cond_symbol, cond_layout.clone(), cond));
|
||||||
|
|
||||||
|
// NOTE this will still store shadowed names. I think that is fine because the branch
|
||||||
|
// will throw an error anyway.
|
||||||
|
let ret = match store_pattern2(
|
||||||
|
env,
|
||||||
|
&mono_pattern,
|
||||||
|
cond_symbol,
|
||||||
|
0,
|
||||||
|
cond_layout,
|
||||||
|
&mut stored,
|
||||||
|
) {
|
||||||
|
Ok(_) => from_can(env, loc_branch.value, procs, None),
|
||||||
|
Err(message) => Expr::RuntimeError(env.arena.alloc(message)),
|
||||||
|
};
|
||||||
|
|
||||||
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
||||||
}
|
}
|
||||||
2 => {
|
|
||||||
let loc_branches: std::vec::Vec<_> = branches
|
|
||||||
.iter()
|
|
||||||
.map(|(loc_branch, _)| {
|
|
||||||
Located::at(loc_branch.region, from_can_pattern(env, &loc_branch.value))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
match crate::pattern::check(Region::zero(), &loc_branches) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(errors) => panic!("Errors in patterns: {:?}", errors),
|
|
||||||
}
|
|
||||||
|
|
||||||
// A when-expression with exactly 2 branches compiles to a Cond.
|
|
||||||
let arena = env.arena;
|
|
||||||
let mut iter = branches.into_iter();
|
|
||||||
let (can_loc_when_pat1, loc_then) = iter.next().unwrap();
|
|
||||||
let (can_loc_when_pat2, loc_else) = iter.next().unwrap();
|
|
||||||
|
|
||||||
let when_pat1 = from_can_pattern(env, &can_loc_when_pat1.value);
|
|
||||||
let when_pat2 = from_can_pattern(env, &can_loc_when_pat2.value);
|
|
||||||
|
|
||||||
let cond_layout = Layout::Builtin(Builtin::Bool(
|
|
||||||
TagName::Global("False".into()),
|
|
||||||
TagName::Global("True".into()),
|
|
||||||
));
|
|
||||||
|
|
||||||
match (&when_pat1, &when_pat2) {
|
|
||||||
(IntLiteral(int), Underscore) => {
|
|
||||||
let cond_lhs = from_can(env, loc_cond.value, procs, None);
|
|
||||||
let cond_rhs = Expr::Int(*int);
|
|
||||||
|
|
||||||
let cond = arena.alloc(Expr::CallByName(
|
|
||||||
Symbol::INT_EQ_I64,
|
|
||||||
arena.alloc([
|
|
||||||
(cond_lhs, Layout::Builtin(Builtin::Int64)),
|
|
||||||
(cond_rhs, Layout::Builtin(Builtin::Int64)),
|
|
||||||
]),
|
|
||||||
));
|
|
||||||
|
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
|
||||||
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
|
||||||
});
|
|
||||||
|
|
||||||
Expr::Cond {
|
|
||||||
cond_layout,
|
|
||||||
cond,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
ret_layout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(FloatLiteral(float), Underscore) => {
|
|
||||||
let cond_lhs = from_can(env, loc_cond.value, procs, None);
|
|
||||||
let cond_rhs = Expr::Float(*float);
|
|
||||||
|
|
||||||
let cond = arena.alloc(Expr::CallByName(
|
|
||||||
Symbol::FLOAT_EQ,
|
|
||||||
arena.alloc([
|
|
||||||
(cond_lhs, Layout::Builtin(Builtin::Float64)),
|
|
||||||
(cond_rhs, Layout::Builtin(Builtin::Float64)),
|
|
||||||
]),
|
|
||||||
));
|
|
||||||
|
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
|
||||||
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
|
||||||
});
|
|
||||||
|
|
||||||
Expr::Cond {
|
|
||||||
cond_layout,
|
|
||||||
cond,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
ret_layout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("TODO handle more conds");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
let loc_branches: std::vec::Vec<_> = branches
|
let cond_layout = Layout::from_var(env.arena, cond_var, env.subs, env.pointer_size)
|
||||||
.iter()
|
.unwrap_or_else(|err| panic!("TODO turn this into a RuntimeError {:?}", err));
|
||||||
.map(|(loc_branch, _)| {
|
|
||||||
Located::at(loc_branch.region, from_can_pattern(env, &loc_branch.value))
|
let cond = from_can(env, loc_cond.value, procs, None);
|
||||||
})
|
let cond_symbol = env.fresh_symbol();
|
||||||
.collect();
|
|
||||||
|
let mut loc_branches = std::vec::Vec::new();
|
||||||
|
let mut opt_branches = std::vec::Vec::new();
|
||||||
|
|
||||||
|
for (loc_pattern, loc_expr) in branches {
|
||||||
|
let mono_pattern = from_can_pattern(env, &loc_pattern.value);
|
||||||
|
|
||||||
|
loc_branches.push(Located::at(loc_pattern.region, mono_pattern.clone()));
|
||||||
|
|
||||||
|
let mut stores = Vec::with_capacity_in(1, env.arena);
|
||||||
|
|
||||||
|
let mono_expr = match store_pattern2(
|
||||||
|
env,
|
||||||
|
&mono_pattern,
|
||||||
|
cond_symbol,
|
||||||
|
0,
|
||||||
|
cond_layout.clone(),
|
||||||
|
&mut stores,
|
||||||
|
) {
|
||||||
|
Ok(_) => Expr::Store(
|
||||||
|
stores.into_bump_slice(),
|
||||||
|
env.arena.alloc(from_can(env, loc_expr.value, procs, None)),
|
||||||
|
),
|
||||||
|
Err(message) => Expr::RuntimeError(env.arena.alloc(message)),
|
||||||
|
};
|
||||||
|
|
||||||
|
opt_branches.push((mono_pattern, mono_expr));
|
||||||
|
}
|
||||||
|
|
||||||
match crate::pattern::check(Region::zero(), &loc_branches) {
|
match crate::pattern::check(Region::zero(), &loc_branches) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(errors) => panic!("Errors in patterns: {:?}", errors),
|
Err(errors) => panic!("Errors in patterns: {:?}", errors),
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a when-expression with 3+ branches.
|
let ret_layout = Layout::from_var(env.arena, expr_var, env.subs, env.pointer_size)
|
||||||
let arena = env.arena;
|
.unwrap_or_else(|err| panic!("TODO turn this into a RuntimeError {:?}", err));
|
||||||
let cond = from_can(env, loc_cond.value, procs, None);
|
|
||||||
let subs = &env.subs;
|
|
||||||
let layout = Layout::from_var(arena, cond_var, subs, env.pointer_size)
|
|
||||||
.unwrap_or_else(|_| panic!("TODO generate a runtime error in from_can_when here!"));
|
|
||||||
|
|
||||||
// We can Switch on integers and tags, because they both have
|
let branching = crate::decision_tree::optimize_when(
|
||||||
// representations that work as integer values.
|
env,
|
||||||
//
|
cond_symbol,
|
||||||
// TODO we can also Switch on record fields if we're pattern matching
|
cond_layout.clone(),
|
||||||
// on a record field that's also Switchable.
|
ret_layout,
|
||||||
//
|
opt_branches,
|
||||||
// TODO we can also convert floats to integer representations.
|
);
|
||||||
let is_switchable = match layout {
|
|
||||||
Layout::Builtin(Builtin::Int64) => true,
|
|
||||||
Layout::Builtin(Builtin::Bool(_, _)) => true,
|
|
||||||
Layout::Builtin(Builtin::Byte(_)) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the condition is an Int or Float, we can potentially use
|
let stores = env.arena.alloc([(cond_symbol, cond_layout, cond)]);
|
||||||
// a Switch for more efficiency.
|
|
||||||
if is_switchable {
|
|
||||||
// These are integer literals or underscore patterns,
|
|
||||||
// so they're eligible for user in a jump table.
|
|
||||||
let mut jumpable_branches = Vec::with_capacity_in(branches.len(), arena);
|
|
||||||
let mut opt_default_branch = None;
|
|
||||||
|
|
||||||
let mut is_last = true;
|
Expr::Store(stores, env.arena.alloc(branching))
|
||||||
for (loc_when_pat, loc_expr) in branches.into_iter().rev() {
|
|
||||||
let mono_expr = from_can(env, loc_expr.value, procs, None);
|
|
||||||
let when_pat = from_can_pattern(env, &loc_when_pat.value);
|
|
||||||
|
|
||||||
if is_last {
|
|
||||||
opt_default_branch = match &when_pat {
|
|
||||||
Identifier(symbol) => {
|
|
||||||
// TODO does this evaluate `cond` twice?
|
|
||||||
Some(arena.alloc(Expr::Store(
|
|
||||||
arena.alloc([(*symbol, layout.clone(), cond.clone())]),
|
|
||||||
arena.alloc(mono_expr.clone()),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
Shadowed(_region, _ident) => {
|
|
||||||
panic!("TODO make runtime exception out of the branch");
|
|
||||||
}
|
|
||||||
_ => Some(arena.alloc(mono_expr.clone())),
|
|
||||||
};
|
|
||||||
is_last = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match &when_pat {
|
|
||||||
IntLiteral(int) => {
|
|
||||||
// Switch only compares the condition to the
|
|
||||||
// alternatives based on their bit patterns,
|
|
||||||
// so casting from i64 to u64 makes no difference here.
|
|
||||||
jumpable_branches.push((*int as u64, mono_expr));
|
|
||||||
}
|
|
||||||
BitLiteral(v) => jumpable_branches.push((*v as u64, mono_expr)),
|
|
||||||
EnumLiteral { tag_id, .. } => {
|
|
||||||
jumpable_branches.push((*tag_id as u64, mono_expr))
|
|
||||||
}
|
|
||||||
Identifier(_) => {
|
|
||||||
// store is handled above
|
|
||||||
}
|
|
||||||
Underscore => {}
|
|
||||||
Shadowed(_, _) => {
|
|
||||||
panic!("TODO runtime error for shadowing in a pattern");
|
|
||||||
}
|
|
||||||
UnsupportedPattern(_region) => unreachable!("When accepts all patterns"),
|
|
||||||
AppliedTag { .. }
|
|
||||||
| StrLiteral(_)
|
|
||||||
| RecordDestructure(_, _)
|
|
||||||
| FloatLiteral(_) => {
|
|
||||||
// The type checker should have converted these mismatches into RuntimeErrors already!
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
panic!("A type mismatch in a pattern was not converted to a runtime error: {:?}", when_pat);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the default branch was never set, that means
|
|
||||||
// our canonical Expr didn't have one. An earlier
|
|
||||||
// step in the compilation process should have
|
|
||||||
// ruled this out!
|
|
||||||
debug_assert!(opt_default_branch.is_some());
|
|
||||||
let default_branch = opt_default_branch.unwrap();
|
|
||||||
|
|
||||||
let cond_layout = Layout::from_var(arena, cond_var, env.subs, env.pointer_size)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
panic!("TODO turn cond_layout into a RuntimeError {:?}", err)
|
|
||||||
});
|
|
||||||
|
|
||||||
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
panic!("TODO turn ret_layout into a RuntimeError {:?}", err)
|
|
||||||
});
|
|
||||||
|
|
||||||
Expr::Switch {
|
|
||||||
cond: arena.alloc(cond),
|
|
||||||
branches: jumpable_branches.into_bump_slice(),
|
|
||||||
default_branch,
|
|
||||||
ret_layout,
|
|
||||||
cond_layout,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// /// More than two conditional branches, e.g. a 3-way when-expression
|
|
||||||
// Expr::Branches {
|
|
||||||
// /// The left-hand side of the conditional. We compile this to LLVM once,
|
|
||||||
// /// then reuse it to test against each different compiled cond_rhs value.
|
|
||||||
// cond_lhs: &'a Expr<'a>,
|
|
||||||
// /// ( cond_rhs, pass, fail )
|
|
||||||
// branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)],
|
|
||||||
// ret_var: Variable,
|
|
||||||
// },
|
|
||||||
panic!(
|
|
||||||
"TODO support when-expressions of 3+ branches whose conditions aren't integers."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1203,25 +1195,28 @@ fn specialize_proc_body<'a>(
|
||||||
|
|
||||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||||
/// codegen can generate a runtime error if this pattern is reached.
|
/// codegen can generate a runtime error if this pattern is reached.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Pattern<'a> {
|
pub enum Pattern<'a> {
|
||||||
Identifier(Symbol),
|
Identifier(Symbol),
|
||||||
AppliedTag {
|
Underscore,
|
||||||
tag_name: TagName,
|
|
||||||
arguments: Vec<'a, Pattern<'a>>,
|
IntLiteral(i64),
|
||||||
layout: Layout<'a>,
|
FloatLiteral(u64),
|
||||||
union: crate::pattern::Union,
|
|
||||||
},
|
|
||||||
BitLiteral(bool),
|
BitLiteral(bool),
|
||||||
EnumLiteral {
|
EnumLiteral {
|
||||||
tag_id: u8,
|
tag_id: u8,
|
||||||
enum_size: u8,
|
enum_size: u8,
|
||||||
},
|
},
|
||||||
IntLiteral(i64),
|
|
||||||
FloatLiteral(f64),
|
|
||||||
StrLiteral(Box<str>),
|
StrLiteral(Box<str>),
|
||||||
|
|
||||||
RecordDestructure(Vec<'a, RecordDestruct<'a>>, Layout<'a>),
|
RecordDestructure(Vec<'a, RecordDestruct<'a>>, Layout<'a>),
|
||||||
Underscore,
|
AppliedTag {
|
||||||
|
tag_name: TagName,
|
||||||
|
tag_id: u8,
|
||||||
|
arguments: Vec<'a, (Pattern<'a>, Layout<'a>)>,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
union: crate::pattern::Union,
|
||||||
|
},
|
||||||
|
|
||||||
// Runtime Exceptions
|
// Runtime Exceptions
|
||||||
Shadowed(Region, Located<Ident>),
|
Shadowed(Region, Located<Ident>),
|
||||||
|
@ -1229,7 +1224,7 @@ pub enum Pattern<'a> {
|
||||||
UnsupportedPattern(Region),
|
UnsupportedPattern(Region),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct RecordDestruct<'a> {
|
pub struct RecordDestruct<'a> {
|
||||||
pub label: Lowercase,
|
pub label: Lowercase,
|
||||||
pub symbol: Symbol,
|
pub symbol: Symbol,
|
||||||
|
@ -1245,14 +1240,14 @@ fn from_can_pattern<'a>(
|
||||||
Underscore => Pattern::Underscore,
|
Underscore => Pattern::Underscore,
|
||||||
Identifier(symbol) => Pattern::Identifier(*symbol),
|
Identifier(symbol) => Pattern::Identifier(*symbol),
|
||||||
IntLiteral(v) => Pattern::IntLiteral(*v),
|
IntLiteral(v) => Pattern::IntLiteral(*v),
|
||||||
FloatLiteral(v) => Pattern::FloatLiteral(*v),
|
FloatLiteral(v) => Pattern::FloatLiteral(f64::to_bits(*v)),
|
||||||
StrLiteral(v) => Pattern::StrLiteral(v.clone()),
|
StrLiteral(v) => Pattern::StrLiteral(v.clone()),
|
||||||
Shadowed(region, ident) => Pattern::Shadowed(*region, ident.clone()),
|
Shadowed(region, ident) => Pattern::Shadowed(*region, ident.clone()),
|
||||||
UnsupportedPattern(region) => Pattern::UnsupportedPattern(*region),
|
UnsupportedPattern(region) => Pattern::UnsupportedPattern(*region),
|
||||||
|
|
||||||
NumLiteral(var, num) => match to_int_or_float(env.subs, *var) {
|
NumLiteral(var, num) => match to_int_or_float(env.subs, *var) {
|
||||||
IntOrFloat::IntType => Pattern::IntLiteral(*num),
|
IntOrFloat::IntType => Pattern::IntLiteral(*num),
|
||||||
IntOrFloat::FloatType => Pattern::FloatLiteral(*num as f64),
|
IntOrFloat::FloatType => Pattern::FloatLiteral(*num as u64),
|
||||||
},
|
},
|
||||||
|
|
||||||
AppliedTag {
|
AppliedTag {
|
||||||
|
@ -1260,54 +1255,70 @@ fn from_can_pattern<'a>(
|
||||||
tag_name,
|
tag_name,
|
||||||
arguments,
|
arguments,
|
||||||
..
|
..
|
||||||
} => match Layout::from_var(env.arena, *whole_var, env.subs, env.pointer_size) {
|
} => {
|
||||||
Ok(Layout::Builtin(Builtin::Bool(_bottom, top))) => {
|
let mut fields = std::vec::Vec::new();
|
||||||
Pattern::BitLiteral(tag_name == &top)
|
|
||||||
|
match roc_types::pretty_print::chase_ext_tag_union(env.subs, *whole_var, &mut fields) {
|
||||||
|
Ok(()) | Err((_, Content::FlexVar(_))) => {}
|
||||||
|
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
||||||
}
|
}
|
||||||
Ok(Layout::Builtin(Builtin::Byte(conversion))) => match conversion.get(&tag_name) {
|
|
||||||
Some(index) => Pattern::EnumLiteral {
|
fields.sort();
|
||||||
tag_id: *index,
|
let tag_id = fields
|
||||||
enum_size: conversion.len() as u8,
|
.iter()
|
||||||
|
.position(|(key, _)| key == tag_name)
|
||||||
|
.expect("tag must be in its own type");
|
||||||
|
|
||||||
|
let enum_size = fields.len();
|
||||||
|
|
||||||
|
let mut ctors = std::vec::Vec::with_capacity(fields.len());
|
||||||
|
for (tag_name, args) in &fields {
|
||||||
|
ctors.push(Ctor {
|
||||||
|
name: tag_name.clone(),
|
||||||
|
arity: args.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let union = crate::pattern::Union {
|
||||||
|
alternatives: ctors,
|
||||||
|
};
|
||||||
|
|
||||||
|
let fields_map: MutMap<_, _> = fields.into_iter().collect();
|
||||||
|
|
||||||
|
match crate::layout::layout_from_tag_union(
|
||||||
|
env.arena,
|
||||||
|
&fields_map,
|
||||||
|
env.subs,
|
||||||
|
env.pointer_size,
|
||||||
|
) {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool)) => Pattern::BitLiteral(tag_id != 0),
|
||||||
|
Ok(Layout::Builtin(Builtin::Byte)) => Pattern::EnumLiteral {
|
||||||
|
tag_id: tag_id as u8,
|
||||||
|
enum_size: enum_size as u8,
|
||||||
},
|
},
|
||||||
None => unreachable!("Tag must be in its own type"),
|
Ok(layout) => {
|
||||||
},
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
Ok(layout) => {
|
for (pat_var, loc_pat) in arguments {
|
||||||
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
let layout =
|
||||||
for (_, loc_pat) in arguments {
|
Layout::from_var(env.arena, *pat_var, env.subs, env.pointer_size)
|
||||||
mono_args.push(from_can_pattern(env, &loc_pat.value));
|
.unwrap_or_else(|err| {
|
||||||
}
|
panic!("TODO turn pat_var into a RuntimeError {:?}", err)
|
||||||
|
});
|
||||||
|
|
||||||
let mut fields = std::vec::Vec::new();
|
mono_args.push((from_can_pattern(env, &loc_pat.value), layout));
|
||||||
let union = match roc_types::pretty_print::chase_ext_tag_union(
|
|
||||||
env.subs,
|
|
||||||
*whole_var,
|
|
||||||
&mut fields,
|
|
||||||
) {
|
|
||||||
Ok(()) | Err((_, Content::FlexVar(_))) => {
|
|
||||||
let mut ctors = std::vec::Vec::with_capacity(fields.len());
|
|
||||||
for (tag_name, args) in fields {
|
|
||||||
ctors.push(crate::pattern::Ctor {
|
|
||||||
name: tag_name.clone(),
|
|
||||||
arity: args.len(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::pattern::Union {
|
|
||||||
alternatives: ctors,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
|
||||||
};
|
|
||||||
|
|
||||||
Pattern::AppliedTag {
|
Pattern::AppliedTag {
|
||||||
tag_name: tag_name.clone(),
|
tag_name: tag_name.clone(),
|
||||||
arguments: mono_args,
|
tag_id: tag_id as u8,
|
||||||
union,
|
arguments: mono_args,
|
||||||
layout,
|
union,
|
||||||
|
layout,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Err(()) => panic!("Invalid layout"),
|
||||||
}
|
}
|
||||||
Err(()) => panic!("Invalid layout"),
|
}
|
||||||
},
|
|
||||||
|
|
||||||
RecordDestructure {
|
RecordDestructure {
|
||||||
whole_var,
|
whole_var,
|
||||||
|
|
|
@ -6,22 +6,21 @@ use roc_module::symbol::Symbol;
|
||||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||||
|
|
||||||
/// Types for code gen must be monomorphic. No type variables allowed!
|
/// Types for code gen must be monomorphic. No type variables allowed!
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Layout<'a> {
|
pub enum Layout<'a> {
|
||||||
Builtin(Builtin<'a>),
|
Builtin(Builtin<'a>),
|
||||||
Struct(&'a [(Lowercase, Layout<'a>)]),
|
Struct(&'a [(Lowercase, Layout<'a>)]),
|
||||||
Tag(&'a [Layout<'a>]),
|
Union(&'a [&'a [Layout<'a>]]),
|
||||||
Pointer(&'a Layout<'a>),
|
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// A function. The types of its arguments, then the type of its return value.
|
||||||
FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>),
|
FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Builtin<'a> {
|
pub enum Builtin<'a> {
|
||||||
Int64,
|
Int64,
|
||||||
Float64,
|
Float64,
|
||||||
Bool(TagName, TagName),
|
Bool,
|
||||||
Byte(MutMap<TagName, u8>),
|
Byte,
|
||||||
Str,
|
Str,
|
||||||
Map(&'a Layout<'a>, &'a Layout<'a>),
|
Map(&'a Layout<'a>, &'a Layout<'a>),
|
||||||
Set(&'a Layout<'a>),
|
Set(&'a Layout<'a>),
|
||||||
|
@ -79,6 +78,24 @@ impl<'a> Layout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn safe_to_memcpy(&self) -> bool {
|
||||||
|
use Layout::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Builtin(builtin) => builtin.safe_to_memcpy(),
|
||||||
|
Struct(fields) => fields
|
||||||
|
.iter()
|
||||||
|
.all(|(_, field_layout)| field_layout.safe_to_memcpy()),
|
||||||
|
Union(tags) => tags
|
||||||
|
.iter()
|
||||||
|
.all(|tag_layout| tag_layout.iter().all(|field| field.safe_to_memcpy())),
|
||||||
|
FunctionPointer(_, _) => {
|
||||||
|
// Function pointers are immutable and can always be safely copied
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stack_size(&self, pointer_size: u32) -> u32 {
|
pub fn stack_size(&self, pointer_size: u32) -> u32 {
|
||||||
use Layout::*;
|
use Layout::*;
|
||||||
|
|
||||||
|
@ -93,17 +110,17 @@ impl<'a> Layout<'a> {
|
||||||
|
|
||||||
sum
|
sum
|
||||||
}
|
}
|
||||||
Tag(fields) => {
|
Union(fields) => fields
|
||||||
// the symbol is a 64-bit value, so 8 bytes
|
.iter()
|
||||||
let mut sum = 8;
|
.map(|tag_layout| {
|
||||||
|
tag_layout
|
||||||
for field_layout in *fields {
|
.iter()
|
||||||
sum += field_layout.stack_size(pointer_size);
|
.map(|field| field.stack_size(pointer_size))
|
||||||
}
|
.sum()
|
||||||
|
})
|
||||||
sum
|
.max()
|
||||||
}
|
.unwrap_or_default(),
|
||||||
Pointer(_) | FunctionPointer(_, _) => pointer_size,
|
FunctionPointer(_, _) => pointer_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,17 +148,25 @@ impl<'a> Builtin<'a> {
|
||||||
match self {
|
match self {
|
||||||
Int64 => Builtin::I64_SIZE,
|
Int64 => Builtin::I64_SIZE,
|
||||||
Float64 => Builtin::F64_SIZE,
|
Float64 => Builtin::F64_SIZE,
|
||||||
Bool(_, _) => Builtin::BOOL_SIZE,
|
Bool => Builtin::BOOL_SIZE,
|
||||||
Byte(_) => Builtin::BYTE_SIZE,
|
Byte => Builtin::BYTE_SIZE,
|
||||||
Str | EmptyStr => Builtin::STR_WORDS * pointer_size,
|
Str | EmptyStr => Builtin::STR_WORDS * pointer_size,
|
||||||
Map(_, _) | EmptyMap => Builtin::MAP_WORDS * pointer_size,
|
Map(_, _) | EmptyMap => Builtin::MAP_WORDS * pointer_size,
|
||||||
Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size,
|
Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size,
|
||||||
List(_) | EmptyList => Builtin::LIST_WORDS * pointer_size,
|
List(_) | EmptyList => Builtin::LIST_WORDS * pointer_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn safe_to_memcpy(&self) -> bool {
|
||||||
|
use Builtin::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Int64 | Float64 | Bool | Byte | EmptyStr | EmptyMap | EmptyList | EmptySet => true,
|
||||||
|
Str | Map(_, _) | Set(_) | List(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
|
||||||
fn layout_from_flat_type<'a>(
|
fn layout_from_flat_type<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
flat_type: FlatType,
|
flat_type: FlatType,
|
||||||
|
@ -274,81 +299,7 @@ fn layout_from_flat_type<'a>(
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||||
|
|
||||||
match tags.len() {
|
layout_from_tag_union(arena, &tags, subs, pointer_size)
|
||||||
0 => {
|
|
||||||
panic!("TODO gracefully handle trying to instantiate Never");
|
|
||||||
}
|
|
||||||
// We can only unwrap a wrapper if it never becomes part of a bigger union
|
|
||||||
// therefore, the ext_var must be the literal empty tag union
|
|
||||||
1 => {
|
|
||||||
// This is a wrapper. Unwrap it!
|
|
||||||
let (tag, args) = tags.into_iter().next().unwrap();
|
|
||||||
|
|
||||||
match tag {
|
|
||||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
|
||||||
debug_assert!(args.len() == 1);
|
|
||||||
|
|
||||||
let var = args.into_iter().next().unwrap();
|
|
||||||
|
|
||||||
unwrap_num_tag(subs, var)
|
|
||||||
}
|
|
||||||
TagName::Private(symbol) => {
|
|
||||||
panic!("TODO emit wrapped private tag for {:?} {:?}", symbol, args);
|
|
||||||
}
|
|
||||||
TagName::Global(ident) => {
|
|
||||||
panic!("TODO emit wrapped global tag for {:?} {:?}", ident, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Check if we can turn this tag union into an enum
|
|
||||||
// The arguments of all tags must have size 0.
|
|
||||||
// That is trivially the case when there are no arguments
|
|
||||||
//
|
|
||||||
// [ Orange, Apple, Banana ]
|
|
||||||
//
|
|
||||||
// But when one-tag tag unions are optimized away, we can also use an enum for
|
|
||||||
//
|
|
||||||
// [ Foo [ Unit ], Bar [ Unit ] ]
|
|
||||||
|
|
||||||
let arguments_have_size_0 = || {
|
|
||||||
tags.iter().all(|(_, args)| {
|
|
||||||
args.iter().all(|var| {
|
|
||||||
Layout::from_var(arena, *var, subs, pointer_size)
|
|
||||||
.map(|v| v.stack_size(pointer_size))
|
|
||||||
== Ok(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// up to 256 enum keys can be stored in a byte
|
|
||||||
if tags.len() <= std::u8::MAX as usize + 1 && arguments_have_size_0() {
|
|
||||||
if tags.len() <= 2 {
|
|
||||||
// Up to 2 enum tags can be stored (in theory) in one bit
|
|
||||||
let mut it = tags.keys();
|
|
||||||
let a: TagName = it.next().unwrap().clone();
|
|
||||||
let b: TagName = it.next().unwrap().clone();
|
|
||||||
|
|
||||||
if a < b {
|
|
||||||
Ok(Layout::Builtin(Builtin::Bool(a, b)))
|
|
||||||
} else {
|
|
||||||
Ok(Layout::Builtin(Builtin::Bool(b, a)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// up to 256 enum tags can be stored in a byte
|
|
||||||
let mut tag_to_u8 = MutMap::default();
|
|
||||||
|
|
||||||
for (counter, (name, _)) in tags.into_iter().enumerate() {
|
|
||||||
tag_to_u8.insert(name, counter as u8);
|
|
||||||
}
|
|
||||||
Ok(Layout::Builtin(Builtin::Byte(tag_to_u8)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// panic!("TODO handle a tag union with mutliple tags: {:?}", tags);
|
|
||||||
Ok(Layout::Tag(&[]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, _, _) => {
|
RecursiveTagUnion(_, _, _) => {
|
||||||
panic!("TODO make Layout for non-empty Tag Union");
|
panic!("TODO make Layout for non-empty Tag Union");
|
||||||
|
@ -364,6 +315,100 @@ fn layout_from_flat_type<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn layout_from_tag_union<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
tags: &MutMap<TagName, std::vec::Vec<Variable>>,
|
||||||
|
subs: &Subs,
|
||||||
|
pointer_size: u32,
|
||||||
|
) -> Result<Layout<'a>, ()> {
|
||||||
|
match tags.len() {
|
||||||
|
0 => {
|
||||||
|
panic!("TODO gracefully handle trying to instantiate Never");
|
||||||
|
}
|
||||||
|
// We can only unwrap a wrapper if it never becomes part of a bigger union
|
||||||
|
// therefore, the ext_var must be the literal empty tag union
|
||||||
|
1 => {
|
||||||
|
// This is a wrapper. Unwrap it!
|
||||||
|
let (tag_name, arguments) = tags.iter().next().unwrap();
|
||||||
|
|
||||||
|
match &tag_name {
|
||||||
|
TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||||
|
debug_assert!(arguments.len() == 1);
|
||||||
|
|
||||||
|
let var = arguments.iter().next().unwrap();
|
||||||
|
|
||||||
|
unwrap_num_tag(subs, *var)
|
||||||
|
}
|
||||||
|
TagName::Private(_) | TagName::Global(_) => {
|
||||||
|
let mut arg_layouts = Vec::with_capacity_in(arguments.len(), arena);
|
||||||
|
|
||||||
|
for arg in arguments {
|
||||||
|
arg_layouts.push(Layout::from_var(arena, *arg, subs, pointer_size)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let layouts = [arg_layouts.into_bump_slice()];
|
||||||
|
|
||||||
|
Ok(Layout::Union(arena.alloc(layouts)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Check if we can turn this tag union into an enum
|
||||||
|
// The arguments of all tags must have size 0.
|
||||||
|
// That is trivially the case when there are no arguments
|
||||||
|
//
|
||||||
|
// [ Orange, Apple, Banana ]
|
||||||
|
//
|
||||||
|
// But when one-tag tag unions are optimized away, we can also use an enum for
|
||||||
|
//
|
||||||
|
// [ Foo [ Unit ], Bar [ Unit ] ]
|
||||||
|
|
||||||
|
let arguments_have_size_0 = || {
|
||||||
|
tags.iter().all(|(_, args)| {
|
||||||
|
args.iter().all(|var| {
|
||||||
|
Layout::from_var(arena, *var, subs, pointer_size)
|
||||||
|
.map(|v| v.stack_size(pointer_size))
|
||||||
|
== Ok(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// up to 256 enum keys can be stored in a byte
|
||||||
|
if tags.len() <= std::u8::MAX as usize + 1 && arguments_have_size_0() {
|
||||||
|
if tags.len() <= 2 {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool))
|
||||||
|
} else {
|
||||||
|
// up to 256 enum tags can be stored in a byte
|
||||||
|
Ok(Layout::Builtin(Builtin::Byte))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let add_discriminant = tags.len() != 1;
|
||||||
|
let mut layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
|
for arguments in tags.values() {
|
||||||
|
// add a field for the discriminant if there is more than one tag in the union
|
||||||
|
let mut arg_layouts = if add_discriminant {
|
||||||
|
let discriminant = Layout::Builtin(Builtin::Int64);
|
||||||
|
let mut result = Vec::with_capacity_in(arguments.len() + 1, arena);
|
||||||
|
result.push(discriminant);
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
Vec::with_capacity_in(arguments.len(), arena)
|
||||||
|
};
|
||||||
|
|
||||||
|
for arg in arguments {
|
||||||
|
arg_layouts.push(Layout::from_var(arena, *arg, subs, pointer_size)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
layouts.push(arg_layouts.into_bump_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Layout::Union(arena.alloc(layouts)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ext_var_is_empty_tag_union(subs: &Subs, ext_var: Variable) -> bool {
|
fn ext_var_is_empty_tag_union(subs: &Subs, ext_var: Variable) -> bool {
|
||||||
// the ext_var is empty
|
// the ext_var is empty
|
||||||
let mut ext_fields = std::vec::Vec::new();
|
let mut ext_fields = std::vec::Vec::new();
|
||||||
|
|
|
@ -16,4 +16,6 @@ pub mod layout;
|
||||||
// Temporary, while we can build up test cases and optimize the exhaustiveness checking.
|
// Temporary, while we can build up test cases and optimize the exhaustiveness checking.
|
||||||
// For now, following this warning's advice will lead to nasty type inference errors.
|
// For now, following this warning's advice will lead to nasty type inference errors.
|
||||||
#[allow(clippy::ptr_arg)]
|
#[allow(clippy::ptr_arg)]
|
||||||
|
pub mod decision_tree;
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
pub mod pattern;
|
pub mod pattern;
|
||||||
|
|
|
@ -4,14 +4,15 @@ use roc_region::all::{Located, Region};
|
||||||
|
|
||||||
use self::Pattern::*;
|
use self::Pattern::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Union {
|
pub struct Union {
|
||||||
pub alternatives: Vec<Ctor>,
|
pub alternatives: Vec<Ctor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Ctor {
|
pub struct Ctor {
|
||||||
pub name: TagName,
|
pub name: TagName,
|
||||||
|
// pub tag_id: u8,
|
||||||
pub arity: usize,
|
pub arity: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ pub enum Literal {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Bit(bool),
|
Bit(bool),
|
||||||
Byte(u8),
|
Byte(u8),
|
||||||
Float(f64),
|
Float(u64),
|
||||||
Str(Box<str>),
|
Str(Box<str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let simplified_args: std::vec::Vec<_> =
|
let simplified_args: std::vec::Vec<_> =
|
||||||
arguments.iter().map(|v| simplify(&v)).collect();
|
arguments.iter().map(|v| simplify(&v.0)).collect();
|
||||||
Ctor(union.clone(), tag_name.clone(), simplified_args)
|
Ctor(union.clone(), tag_name.clone(), simplified_args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +255,6 @@ fn recover_ctor(
|
||||||
arity: usize,
|
arity: usize,
|
||||||
mut patterns: Vec<Pattern>,
|
mut patterns: Vec<Pattern>,
|
||||||
) -> Vec<Pattern> {
|
) -> Vec<Pattern> {
|
||||||
// TODO ensure that this behaves the same as haskell's splitAt
|
|
||||||
let mut rest = patterns.split_off(arity);
|
let mut rest = patterns.split_off(arity);
|
||||||
let args = patterns;
|
let args = patterns;
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,6 @@ mod helpers;
|
||||||
mod test_mono {
|
mod test_mono {
|
||||||
use crate::helpers::{can_expr, infer_expr, test_home, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, test_home, CanExprOut};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::MutMap;
|
|
||||||
use roc_module::ident::TagName::*;
|
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::expr::Expr::{self, *};
|
use roc_mono::expr::Expr::{self, *};
|
||||||
use roc_mono::expr::Procs;
|
use roc_mono::expr::Procs;
|
||||||
|
@ -164,7 +162,7 @@ mod test_mono {
|
||||||
|
|
||||||
Cond {
|
Cond {
|
||||||
cond: &Expr::Bool(true),
|
cond: &Expr::Bool(true),
|
||||||
cond_layout: Builtin(Bool(Global("False".into()), Global("True".into()))),
|
cond_layout: Builtin(Bool),
|
||||||
pass: &Expr::Str("bar"),
|
pass: &Expr::Str("bar"),
|
||||||
fail: &Expr::Str("foo"),
|
fail: &Expr::Str("foo"),
|
||||||
ret_layout: Builtin(Str),
|
ret_layout: Builtin(Str),
|
||||||
|
@ -190,11 +188,11 @@ mod test_mono {
|
||||||
|
|
||||||
Cond {
|
Cond {
|
||||||
cond: &Expr::Bool(true),
|
cond: &Expr::Bool(true),
|
||||||
cond_layout: Builtin(Bool(Global("False".into()), Global("True".into()))),
|
cond_layout: Builtin(Bool),
|
||||||
pass: &Expr::Str("bar"),
|
pass: &Expr::Str("bar"),
|
||||||
fail: &Cond {
|
fail: &Cond {
|
||||||
cond: &Expr::Bool(false),
|
cond: &Expr::Bool(false),
|
||||||
cond_layout: Builtin(Bool(Global("False".into()), Global("True".into()))),
|
cond_layout: Builtin(Bool),
|
||||||
pass: &Expr::Str("foo"),
|
pass: &Expr::Str("foo"),
|
||||||
fail: &Expr::Str("baz"),
|
fail: &Expr::Str("baz"),
|
||||||
ret_layout: Builtin(Str),
|
ret_layout: Builtin(Str),
|
||||||
|
@ -228,10 +226,7 @@ mod test_mono {
|
||||||
Builtin(Str),
|
Builtin(Str),
|
||||||
Cond {
|
Cond {
|
||||||
cond: &Expr::Bool(true),
|
cond: &Expr::Bool(true),
|
||||||
cond_layout: Builtin(Bool(
|
cond_layout: Builtin(Bool),
|
||||||
Global("False".into()),
|
|
||||||
Global("True".into()),
|
|
||||||
)),
|
|
||||||
pass: &Expr::Str("bar"),
|
pass: &Expr::Str("bar"),
|
||||||
fail: &Expr::Str("foo"),
|
fail: &Expr::Str("foo"),
|
||||||
ret_layout: Builtin(Str),
|
ret_layout: Builtin(Str),
|
||||||
|
@ -361,11 +356,7 @@ mod test_mono {
|
||||||
let home = test_home();
|
let home = test_home();
|
||||||
let var_x = interns.symbol(home, "x".into());
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
let stores = [(
|
let stores = [(var_x, Layout::Builtin(Builtin::Bool), Bool(true))];
|
||||||
var_x,
|
|
||||||
Layout::Builtin(Builtin::Bool(Global("False".into()), Global("True".into()))),
|
|
||||||
Bool(true),
|
|
||||||
)];
|
|
||||||
|
|
||||||
let load = Load(var_x);
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
@ -389,11 +380,7 @@ mod test_mono {
|
||||||
let home = test_home();
|
let home = test_home();
|
||||||
let var_x = interns.symbol(home, "x".into());
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
let stores = [(
|
let stores = [(var_x, Layout::Builtin(Builtin::Bool), Bool(false))];
|
||||||
var_x,
|
|
||||||
Layout::Builtin(Builtin::Bool(Global("No".into()), Global("Yes".into()))),
|
|
||||||
Bool(false),
|
|
||||||
)];
|
|
||||||
|
|
||||||
let load = Load(var_x);
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
@ -418,13 +405,8 @@ mod test_mono {
|
||||||
let home = test_home();
|
let home = test_home();
|
||||||
let var_x = interns.symbol(home, "x".into());
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
let mut fruits = MutMap::default();
|
// orange gets index (and therefore tag_id) 1
|
||||||
|
let stores = [(var_x, Layout::Builtin(Builtin::Byte), Byte(2))];
|
||||||
fruits.insert(Global("Banana".into()), 0);
|
|
||||||
fruits.insert(Global("Orange".into()), 1);
|
|
||||||
fruits.insert(Global("Apple".into()), 2);
|
|
||||||
|
|
||||||
let stores = [(var_x, Layout::Builtin(Builtin::Byte(fruits)), Byte(1))];
|
|
||||||
|
|
||||||
let load = Load(var_x);
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
@ -466,32 +448,31 @@ mod test_mono {
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn when_on_result() {
|
// fn when_on_result() {
|
||||||
// let arena = Bump::new();
|
// compiles_to(
|
||||||
//
|
|
||||||
// compiles_to_with_interns(
|
|
||||||
// r#"
|
// r#"
|
||||||
// x = Ok 0x3
|
// when 1 is
|
||||||
//
|
// 1 -> 12
|
||||||
// when x is
|
// _ -> 34
|
||||||
// Err _ -> 0
|
|
||||||
// Ok n -> n + 1
|
|
||||||
// "#,
|
// "#,
|
||||||
// |interns| {
|
// {
|
||||||
|
// use self::Builtin::*;
|
||||||
|
// use Layout::Builtin;
|
||||||
// let home = test_home();
|
// let home = test_home();
|
||||||
// let var_x = interns.symbol(home, "x".into());
|
|
||||||
//
|
//
|
||||||
// let mut fruits = MutMap::default();
|
// let gen_symbol_3 = Interns::from_index(home, 3);
|
||||||
|
// let gen_symbol_4 = Interns::from_index(home, 4);
|
||||||
//
|
//
|
||||||
// fruits.insert(Global("Banana".into()), 0);
|
// CallByName(
|
||||||
// fruits.insert(Global("Orange".into()), 1);
|
// gen_symbol_3,
|
||||||
// fruits.insert(Global("Apple".into()), 2);
|
// &[(
|
||||||
//
|
// Struct(&[(
|
||||||
// let stores = [(var_x, Layout::Builtin(Builtin::Byte(fruits)), Byte(1))];
|
// CallByName(gen_symbol_4, &[(Int(4), Builtin(Int64))]),
|
||||||
//
|
// Builtin(Int64),
|
||||||
// let load = Load(var_x);
|
// )]),
|
||||||
//
|
// Layout::Struct(&[("x".into(), Builtin(Int64))]),
|
||||||
// Store(arena.alloc(stores), arena.alloc(load))
|
// )],
|
||||||
|
// )
|
||||||
// },
|
// },
|
||||||
// );
|
// )
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -520,6 +520,11 @@ pub fn chase_ext_tag_union(
|
||||||
|
|
||||||
chase_ext_tag_union(subs, ext_var, fields)
|
chase_ext_tag_union(subs, ext_var, fields)
|
||||||
}
|
}
|
||||||
|
Content::Structure(Apply(Symbol::ATTR_ATTR, arguments)) => {
|
||||||
|
debug_assert!(arguments.len() == 2);
|
||||||
|
chase_ext_tag_union(subs, arguments[1], fields)
|
||||||
|
}
|
||||||
|
Content::Alias(_, _, var) => chase_ext_tag_union(subs, var, fields),
|
||||||
|
|
||||||
content => Err((var, content)),
|
content => Err((var, content)),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue