Merge remote-tracking branch 'origin/trunk' into infer-borrow

This commit is contained in:
Folkert 2020-08-19 23:56:29 +02:00
commit 0e5283efd2
10 changed files with 259 additions and 193 deletions

View File

@ -299,21 +299,20 @@ pub fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<(String, S
panic!("A specialization was still marked InProgress after monomorphization had completed: {:?} with layout {:?}", symbol, layout);
}
Done(proc) => {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
headers.push((proc, fn_val));
}
}
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {
for (proc, fn_val) in headers {
// NOTE: This is here to be uncommented in case verification fails.
// (This approach means we don't have to defensively clone name here.)
//
// println!("\n\nBuilding and then verifying function {}\n\n", name);
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
build_proc(&env, &mut layout_ids, proc, fn_val);
if fn_val.verify(true) {
fpm.run_on(&fn_val);

View File

@ -250,19 +250,18 @@ pub fn gen(
// We have to do this in a separate pass first,
// because their bodies may reference each other.
for ((symbol, layout), proc) in procs.get_specialized_procs(arena) {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
headers.push((proc, fn_val));
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {
for (proc, fn_val) in headers {
// NOTE: This is here to be uncommented in case verification fails.
// (This approach means we don't have to defensively clone name here.)
//
// println!("\n\nBuilding and then verifying function {:?}\n\n", proc);
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
build_proc(&env, &mut layout_ids, proc, fn_val);
if fn_val.verify(true) {
fpm.run_on(&fn_val);

View File

@ -347,8 +347,17 @@ min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
##
## If the given index is outside the bounds of the list, returns the original
## list unmodified.
##
## To drop the element at a given index, instead of replacing it, see #List.drop.
put : List elem, Len, elem -> List elem
## Drops the element at the given index from the list.
##
## This has no effect if the given index is outside the bounds of the list.
##
## To replace the element at a given index, instead of dropping it, see #List.put.
drop : List elem, Len -> List elem
## Adds a new element to the end of the list.
##
## >>> List.append [ "a", "b" ] "c"

View File

@ -14,7 +14,8 @@ len : Set * -> Len
add : Set 'elem, 'elem -> Set 'elem
rem : Set 'elem, 'elem -> Set 'elem
## Drops the given element from the set.
drop : Set 'elem, 'elem -> Set 'elem
## Convert each element in the set to something new, by calling a conversion
## function on each of them. Then return a new set of the converted values.

View File

@ -7,33 +7,36 @@ interface Str exposes [ Str, isEmpty, join ] imports []
##
## _For more advanced use cases like working with raw [code points](https://unicode.org/glossary/#code_point),
## see the [roc/unicode](roc/unicode) package. For locale-specific text
## functions (including capitalizing a string, as capitalization rules vary by locale)
## see the [roc/locale](roc/locale) package._
## functions (including uppercasing strings, as capitalization rules vary by locale;
## in English, `"i"` capitalizes to `"I"`, but [in Turkish](https://en.wikipedia.org/wiki/Dotted_and_dotless_I#In_computing),
## the same `"i"` capitalizes to `"İ"` - as well as sorting strings, which also varies
## by locale; `"ö"` is sorted differently in German and Swedish) 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"
## "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, the documentation for Roc strings intentionally
## avoids it. Instead, we use the term "clusters" as a shorthand for "grapheme clusters."
## Every Unicode string is a sequence of [extended grapheme clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster).
## An extended grapheme cluster represents what a person reading a string might
## call a "character" - like "A" or "ö" or "👩‍👩‍👦‍👦".
## Because the term "character" means different things in different areas of
## programming, and "extended grapheme cluster" is a mouthful, in Roc we use the
## term "grapheme" as a shorthand for the more precise "extended grapheme cluster."
##
## You can get the number of grapheme clusters in a string by calling #Str.countClusters on it:
## You can get the number of graphemes in a string by calling #Str.countGraphemes on it:
##
## Str.countClusters "Roc!"
## Str.countClusters "折り紙"
## Str.countClusters "🕊"
## Str.countGraphemes "Roc!"
## Str.countGraphemes "折り紙"
## Str.countGraphemes "🕊"
##
## > The `countClusters` function walks through the entire string to get its answer,
## > The `countGraphemes` function walks through the entire string to get its answer,
## > so if you want to check whether a string is empty, you'll get much better performance
## > by calling `Str.isEmpty myStr` instead of `Str.countClusters myStr == 0`.
## > by calling `Str.isEmpty myStr` instead of `Str.countGraphemes myStr == 0`.
##
## ### Escape sequences
##
@ -107,7 +110,7 @@ Str : [ @Str ]
##
## If you want to keep all the digits, passing the same float to #Str.num
## will do that.
decimal : Float *, Ulen -> Str
decimal : Float *, Len -> Str
## Split a string around a separator.
##
@ -118,7 +121,7 @@ decimal : Float *, Ulen -> Str
##
## >>> Str.split "1,2,3" ""
##
## To split a string into its individual grapheme clusters, use #Str.clusters
## To split a string into its individual graphemes, use #Str.graphemes
split : Str, Str -> List Str
## Check
@ -136,9 +139,9 @@ endsWith : Str, Str -> Bool
contains : Str, Str -> Bool
anyClusters : Str, (Str -> Bool) -> Bool
anyGraphemes : Str, (Str -> Bool) -> Bool
allClusters : Str, (Str -> Bool) -> Bool
allGraphemes : Str, (Str -> Bool) -> Bool
## Combine
@ -154,60 +157,69 @@ join : List Str -> Str
joinWith : List Str, Str -> Str
## Add to the start of a string until it has at least the given number of
## grapheme clusters.
## graphemes.
##
## >>> Str.padClustersStart "0" 5 "36"
## >>> Str.padGraphemesStart "0" 5 "36"
##
## >>> Str.padClustersStart "0" 1 "36"
## >>> Str.padGraphemesStart "0" 1 "36"
##
## >>> Str.padClustersStart "0" 5 "12345"
## >>> Str.padGraphemesStart "0" 5 "12345"
##
## >>> Str.padClustersStart "✈️"" 5 "👩‍👩‍👦‍👦👩‍👩‍👦‍👦👩‍👩‍👦‍👦"
padClustersStart : Str, Int, Str -> Str
## >>> Str.padGraphemesStart "✈️"" 5 "👩‍👩‍👦‍👦👩‍👩‍👦‍👦👩‍👩‍👦‍👦"
padGraphemesStart : Str, Int, Str -> Str
## Add to the end of a string until it has at least the given number of
## grapheme clusters.
## graphemes.
##
## >>> Str.padClustersStart "0" 5 "36"
## >>> Str.padGraphemesStart "0" 5 "36"
##
## >>> Str.padClustersStart "0" 1 "36"
## >>> Str.padGraphemesStart "0" 1 "36"
##
## >>> Str.padClustersStart "0" 5 "12345"
## >>> Str.padGraphemesStart "0" 5 "12345"
##
## >>> Str.padClustersStart "✈️"" 5 "👩‍👩‍👦‍👦👩‍👩‍👦‍👦👩‍👩‍👦‍👦"
padClustersEnd : Str, Int, Str -> Str
## >>> Str.padGraphemesStart "✈️"" 5 "👩‍👩‍👦‍👦👩‍👩‍👦‍👦👩‍👩‍👦‍👦"
padGraphemesEnd : Str, Int, Str -> Str
## Grapheme Clusters
## Graphemes
## Split a string into its individual grapheme clusters.
## Split a string into its individual graphemes.
##
## >>> Str.clusters "1,2,3"
## >>> Str.graphemes "1,2,3"
##
## >>> Str.clusters "👍👍👍"
## >>> Str.graphemes "👍👍👍"
##
clusters : Str -> List Str
graphemes : Str -> List Str
## Str.countClusters "Roc!" # 4
## Str.countClusters "七巧板" # 3
## Str.countClusters "🕊" # 1
## Str.countGraphemes "Roc!" # 4
## Str.countGraphemes "七巧板" # 3
## Str.countGraphemes "🕊" # 1
## Reverse the order of the string's individual grapheme clusters.
## Reverse the order of the string's individual graphemes.
##
## >>> Str.reverseClusters "1-2-3"
## >>> Str.reverseGraphemes "1-2-3"
##
## >>> Str.reverseClusters "🐦✈️"👩‍👩‍👦‍👦"
reverseClusters : Str -> Str
## >>> Str.reverseGraphemes "🐦✈️"👩‍👩‍👦‍👦"
##
## >>> Str.reversegraphemes "Crème Brûlée"
reverseGraphemes : Str -> Str
foldClusters : Str, { start: state, step: (state, Str -> state) } -> state
## Returns #True if the string begins with a capital letter, and #False otherwise.
## Returns #True if the two strings are equal when ignoring case.
##
## >>> Str.isCapitalized "hi"
## >>> Str.caseInsensitiveEq "hi" "Hi"
isCaseInsensitiveEq : Str, Str -> Bool
isCaseInsensitiveNeq : Str, Str -> Bool
walkGraphemes : Str, { start: state, step: (state, Str -> state) } -> state
## Returns #True if the string begins with an uppercase letter.
##
## >>> Str.isCapitalized "Hi"
##
## >>> Str.isCapitalized " Hi"
##
## >>> Str.isCapitalized "hi"
##
## >>> Str.isCapitalized "Česká"
##
## >>> Str.isCapitalized "Э"
@ -218,18 +230,63 @@ foldClusters : Str, { start: state, step: (state, Str -> state) } -> state
##
## >>> 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.
## Since the rules for how to capitalize a string vary by locale,
## (for example, in English, `"i"` capitalizes to `"I"`, but
## [in Turkish](https://en.wikipedia.org/wiki/Dotted_and_dotless_I#In_computing),
## the same `"i"` capitalizes to `"İ"`) see the [roc/locale](roc/locale) package
## package for functions which capitalize strings.
isCapitalized : Str -> Bool
## ## Code Units
## Returns #True if the string consists entirely of uppercase letters.
##
## Besides grapheme clusters, another way to break down strings is into
## >>> Str.isAllUppercase "hi"
##
## >>> Str.isAllUppercase "Hi"
##
## >>> Str.isAllUppercase "HI"
##
## >>> Str.isAllUppercase " Hi"
##
## >>> Str.isAllUppercase "Česká"
##
## >>> Str.isAllUppercase "Э"
##
## >>> Str.isAllUppercase "東京"
##
## >>> Str.isAllUppercase "🐦"
##
## >>> Str.isAllUppercase ""
isAllUppercase : Str -> Bool
## Returns #True if the string consists entirely of lowercase letters.
##
## >>> Str.isAllLowercase "hi"
##
## >>> Str.isAllLowercase "Hi"
##
## >>> Str.isAllLowercase "HI"
##
## >>> Str.isAllLowercase " Hi"
##
## >>> Str.isAllLowercase "Česká"
##
## >>> Str.isAllLowercase "Э"
##
## >>> Str.isAllLowercase "東京"
##
## >>> Str.isAllLowercase "🐦"
##
## >>> Str.isAllLowercase ""
isAllLowercase : Str -> Bool
## Code Units
## Besides graphemes, another way to break down strings is into
## raw code unit integers.
##
## Code units are no substitute for grapheme clusters!
## Code units are no substitute for graphemes!
## These functions exist to support advanced use cases like those found in
## [roc/unicode](roc/unicode), and using code units when grapheme clusters would
## [roc/unicode](roc/unicode), and using code units when graphemes would
## be more appropriate can very easily lead to bugs.
##
## For example, `Str.countGraphemes "👩‍👩‍👦‍👦"` returns `1`,
@ -239,7 +296,7 @@ isCapitalized : Str -> Bool
## 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.)
## see #Str.split and #Str.graphemes.)
##
## >>> Str.toUtf8 "👩‍👩‍👦‍👦"
##
@ -250,12 +307,12 @@ isCapitalized : Str -> Bool
## >>> 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.
## without creating a #List, see #Str.walkUtf8 and #Str.walkRevUtf8.
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.)
## see #Str.split and #Str.graphemes.)
##
## >>> Str.toUtf16 "👩‍👩‍👦‍👦"
##
@ -266,12 +323,12 @@ toUtf8 : Str -> List U8
## >>> 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.
## without creating a #List, see #Str.walkUtf16 and #Str.walkRevUtf16.
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.)
## see #Str.split and #Str.graphemes.)
##
## >>> Str.toUtf32 "👩‍👩‍👦‍👦"
##
@ -282,13 +339,12 @@ toUtf16 : Str -> List U16
## >>> 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.
## without creating a #List, see #Str.walkUtf32 and #Str.walkRevUtf32.
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.)
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.walkGraphemes.)
##
## Here are the #U8 values that will be passed to `step` when this function is
## called on various strings:
@ -299,11 +355,11 @@ toUtf32 : Str -> List U32
## * `"🐦"` 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
walkUtf8 : 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.)
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.walkGraphemes.)
##
## Here are the #U16 values that will be passed to `step` when this function is
## called on various strings:
@ -314,11 +370,11 @@ foldUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
## * `"🐦"` 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
walkUtf16 : 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.)
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.walkGraphemes.)
##
## Here are the #U32 values that will be passed to `step` when this function is
## called on various strings:
@ -329,12 +385,12 @@ foldUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
## * `"🐦"` 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
walkUtf32 : 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.)
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.walkGraphemes.)
##
## Here are the #U8 values that will be passed to `step` when this function is
## called on various strings:
@ -345,11 +401,11 @@ foldUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
## * `"🐦"` 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
walkRevUtf8 : 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.)
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.walkGraphemes.)
##
## Here are the #U16 values that will be passed to `step` when this function is
## called on various strings:
@ -360,11 +416,11 @@ foldRevUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
## * `"🐦"` 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
walkRevUtf16 : 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.)
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.walkGraphemes.)
##
## Here are the #U32 values that will be passed to `step` when this function is
## called on various strings:
@ -375,4 +431,4 @@ foldRevUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
## * `"🐦"` 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
walkRevUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state

View File

@ -1,7 +1,7 @@
use crate::layout_id::LayoutIds;
use crate::llvm::build_list::{
allocate_list, empty_polymorphic_list, list_append, list_concat, list_get_unsafe, list_join,
list_len, list_prepend, list_repeat, list_reverse, list_set, list_single,
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_get_unsafe,
list_join, list_len, list_prepend, list_repeat, list_reverse, list_set, list_single,
};
use crate::llvm::compare::{build_eq, build_neq};
use crate::llvm::convert::{basic_type_from_layout, collection, get_fn_type, ptr_int};
@ -15,7 +15,7 @@ use inkwell::module::{Linkage, Module};
use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::types::{BasicTypeEnum, FunctionType, IntType, StructType};
use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{FloatValue, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::values::{BasicValue, FloatValue, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::AddressSpace;
use inkwell::{IntPredicate, OptimizationLevel};
use roc_collections::all::{ImMap, MutSet};
@ -221,41 +221,68 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
Str(str_literal) => {
if str_literal.is_empty() {
panic!("TODO build an empty string in LLVM");
empty_list(env)
} else {
let ctx = env.context;
let builder = env.builder;
let str_len = str_literal.len() + 1/* TODO drop the +1 when we have structs and this is no longer a NUL-terminated CString.*/;
let byte_type = ctx.i8_type();
let nul_terminator = byte_type.const_zero();
let len_val = ctx.i64_type().const_int(str_len as u64, false);
let ptr = env
.builder
.build_array_malloc(ctx.i8_type(), len_val, "str_ptr")
.unwrap();
let len_u64 = str_literal.len() as u64;
let elem_layout = Layout::Builtin(Builtin::Int8);
// TODO check if malloc returned null; if so, runtime error for OOM!
let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64;
// Copy the bytes from the string literal into the array
for (index, byte) in str_literal.bytes().enumerate() {
let ptr = {
let bytes_len = elem_bytes * len_u64;
let len_type = env.ptr_int();
let len = len_type.const_int(bytes_len, false);
allocate_list(env, &elem_layout, len)
// TODO check if malloc returned null; if so, runtime error for OOM!
};
// Copy the elements from the list literal into the array
for (index, char) in str_literal.as_bytes().iter().enumerate() {
let val = env
.context
.i8_type()
.const_int(*char as u64, false)
.as_basic_value_enum();
let index_val = ctx.i64_type().const_int(index as u64, false);
let elem_ptr =
unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "byte") };
unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "index") };
builder.build_store(elem_ptr, byte_type.const_int(byte as u64, false));
builder.build_store(elem_ptr, val);
}
// 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 index_val = ctx.i64_type().const_int(str_len as u64 - 1, false);
let elem_ptr =
unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "nul_terminator") };
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let len = BasicValueEnum::IntValue(env.ptr_int().const_int(len_u64, false));
let mut struct_val;
builder.build_store(elem_ptr, nul_terminator);
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
BasicValueEnum::PointerValue(ptr)
// Store the length
struct_val = builder
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
// Bitcast to an array of raw bytes
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
}
}
}
@ -1416,7 +1443,7 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
symbol: Symbol,
layout: &Layout<'a>,
proc: &roc_mono::ir::Proc<'a>,
) -> (FunctionValue<'ctx>, Vec<'a, BasicTypeEnum<'ctx>>) {
) -> FunctionValue<'ctx> {
let args = proc.args;
let arena = env.arena;
let context = &env.context;
@ -1450,15 +1477,14 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
fn_val.set_call_conventions(FAST_CALL_CONV);
}
(fn_val, arg_basic_types)
fn_val
}
pub fn build_proc<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
env: &'a Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
proc: roc_mono::ir::Proc<'a>,
fn_val: FunctionValue<'ctx>,
arg_basic_types: Vec<'a, BasicTypeEnum<'ctx>>,
) {
let args = proc.args;
let context = &env.context;
@ -1472,13 +1498,15 @@ pub fn build_proc<'a, 'ctx, 'env>(
let mut scope = Scope::default();
// Add args to scope
for ((arg_val, arg_type), (layout, arg_symbol)) in
fn_val.get_param_iter().zip(arg_basic_types).zip(args)
{
for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) {
set_name(arg_val, arg_symbol.ident_string(&env.interns));
let alloca =
create_entry_block_alloca(env, fn_val, arg_type, arg_symbol.ident_string(&env.interns));
let alloca = create_entry_block_alloca(
env,
fn_val,
arg_val.get_type(),
arg_symbol.ident_string(&env.interns),
);
builder.build_store(alloca, arg_val);

View File

@ -151,13 +151,9 @@ pub fn basic_type_from_layout<'ctx>(
Float64 => context.f64_type().as_basic_type_enum(),
Float32 => context.f32_type().as_basic_type_enum(),
Float16 => context.f16_type().as_basic_type_enum(),
Str | EmptyStr => context
.i8_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
Map(_, _) | EmptyMap => panic!("TODO layout_to_basic_type for Builtin::Map"),
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"),
List(_, _) => collection(context, ptr_bytes).into(),
List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(),
EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)),
},
}

View File

@ -13,17 +13,11 @@ mod helpers;
#[cfg(test)]
mod gen_primitives {
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
#[test]
fn basic_str() {
assert_evals_to!(
"\"shirt and hat\"",
CString::new("shirt and hat").unwrap().as_c_str(),
*const c_char,
CStr::from_ptr
);
assert_evals_to!("\"\"", "", &'static str);
assert_evals_to!("\"shirt and hat\"", "shirt and hat", &'static str);
}
#[test]

View File

@ -139,15 +139,14 @@ pub fn helper_without_uniqueness<'a>(
// We have to do this in a separate pass first,
// because their bodies may reference each other.
for ((symbol, layout), proc) in procs.drain() {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
headers.push((proc, fn_val));
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
for (proc, fn_val) in headers {
build_proc(&env, &mut layout_ids, proc, fn_val);
if fn_val.verify(true) {
function_pass.run_on(&fn_val);
@ -333,15 +332,14 @@ pub fn helper_with_uniqueness<'a>(
// We have to do this in a separate pass first,
// because their bodies may reference each other.
for ((symbol, layout), proc) in procs.drain() {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
headers.push((proc, fn_val));
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
for (proc, fn_val) in headers {
build_proc(&env, &mut layout_ids, proc, fn_val);
if fn_val.verify(true) {
fpm.run_on(&fn_val);

View File

@ -122,50 +122,6 @@ You can rewrite the above like so:
\UserId id ->
```
## Function equality
In Elm, if you write `(\val -> val) == (\val -> val)`, you currently get a runtime exception
which links to [the `==` docs](https://package.elm-lang.org/packages/elm/core/latest/Basics#==), which explain why this is the current behavior and what the better version will look like.
> OCaml also has the "runtime exception if you compare functions for structural equality" behavior, but unlike Elm, in OCaml this appears to be the long-term design.
In Roc, the design is for function equality to be a compile error, but not tracked visibly
in the type system. In this way it's like tail calls; the compiler tracks them, but
it tracks them in a way that is not exposed to the author directly.
So if you write `(\val -> val) == (\val -> val)` in Roc, you'll get a compile error about "function equality," but not a type mismatch - because the types will be identical (`a -> a` in both cases).
The reason for this design is that in practice, the Elm stopgap runtime exception design has
revealed that this edge case comes up absurdly infrequently - to the point where many seasoned Elm programmers do not even know this is a runtime exception, because they've never even accidentally written code that triggers it!
Clearly since this is detectable at compile time, it's a nicer design to give the feedback
at compile time rather than crashing at runtime. Also, it happens so infrequently that it would
be hard to justify lots of type signatures gaining an `Eq` constraint of some sort -
especially when the syntax for that would be added to the language *solely* for the sake
of this extreme edge case.
Instead the design is to track this behind the scenes only, and give
a separate class of compiler error for it than the "type mismatch" error you'd see
for this in Rust or Haskell.
> It's possible that it would be nice to present a note when printing these types
> in a REPL or editor, e.g.
>
> ```
> > \a, b -> a == b
> <function> : a, a -> Bool
>
> Note: this `a` type variable will not accept types that contain functions
> ```
>
> I can see a similar argument for noting information about whether a function
> is tail-recursive. For example:
>
> ```
> > fibonacci
> <tail-recursive function> : Int -> Int
> ```
## Unbound type variables
In Elm, every type variable is named. For example:
@ -776,25 +732,54 @@ outside a record field. Optionality is a concept that exists only in record fiel
and it's intended for the use case of config records like this. The ergonomics
of destructuring mean this wouldn't be a good fit for data modeling.
## Function equality
In Elm, if you write `(\val -> val) == (\val -> val)`, you currently get a runtime exception
which links to [the `==` docs](https://package.elm-lang.org/packages/elm/core/latest/Basics#==),
which explain why this is the current behavior and what the better version will look like.
> OCaml also has the "runtime exception if you compare functions for structural equality"
> behavior, but unlike Elm, in OCaml this appears to be the long-term design.
In Roc, function equality is a compile error, tracked explicitly in the type system.
Here's the type of Roc's equality function:
```elm
'val, 'val -> Bool
```
Whenever a named type variable in Roc has a `'` at the beginning, that means
it is a *functionless* type - a type which cannot involve functions.
If there are any functions in that type, it's a type mismatch. This is true
whether `val` itself is a function, or if it's a type that wraps a function,
like `{ predicate: (Int -> Bool) }` or `List (Bool -> Bool)`.
So if you write `(\a -> a) == (\a -> a)` in Roc, you'll get a type mismatch.
If you wrap both sides of that `==` in a record or list, you'll still get a
type mismatch.
If a named type variable has a `'` anywhere in a given type, then it must have a `'`
everywhere in that type. So it would be an error to have a type like `x, 'x -> Bool`
because `x` has a `'` in one place but not everywhere.
## Standard Data Structures
Elm has `List`, `Array`, `Set`, and `Dict` in the standard library.
Roc has `List`, `Bytes`, `Set`, and `Map` in the standard library.
Roc has `List`, `Set`, and `Map` in the standard library.
Here are the differences:
* `List` in Roc uses the term "list" the way Python does: to mean an unordered sequence of elements. Roc's `List` is more like Elm's `Array`; under the hood it is a RRB tree - specifically, [this one](https://docs.rs/im/14.0.0/im/vector/index.html). It still uses the `[` `]` syntax for values. Also there is no `::` operator because "cons" is not a notably efficient operation on a RRB tree like it is in a linked list; adding an element to either the beginning or end of a Roc `List` is an amortized constant time operation.
* `Bytes` in Roc works like [`Bytes` in `elm/bytes`](https://package.elm-lang.org/packages/elm/bytes/latest/Bytes). It's in the standard library because Roc does not have a concept analogous to Elm's `Kernel`, so if `Bytes` weren't in the standard library, it could not exist as a separate package (and thus operating on raw byte streams would not be supported in Roc).
* `Map` in Roc is like `Dict` in Elm, except it's backed by hashing rather than ordering. Like `List`, it uses [one of Bodil Stokke's `im-rs` persistent data structures](https://docs.rs/im/14.0.0/im/hashmap/index.html) under the hood. Roc also silently computes hash values for any value that can be used with `==`, so there is no `comparable` (or similar) constraint on `Map` keys in Roc.
* `Set` in Roc is like `Set` in Elm: it's shorthand for a `Map` with keys but no value, and it has a slightly different API. Like with `Map`, there is no `comparable` (or similar) constraint on the values that can go in a Roc `Set`.
* `List` in Roc uses the term "list" the way Python does: to mean an unordered sequence of elements. Roc's `List` is more like an array, in that all the elements are sequential in memory and can be accessed in constant time. It still uses the `[` `]` syntax for list literals. Also there is no `::` operator because "cons" is not an efficient operation on an array like it is in a linked list.
* `Map` in Roc is like `Dict` in Elm, except it's backed by hashing rather than ordering. Roc silently computes hash values for any value that can be used with `==`, so instead of a `comparable` constraint on `Set` elements and `Map` keys, in Roc they instead have the *functionless* constraint indicated with a `'`. So to add to a `Set` you use `Set.add : Set 'elem, 'elem -> Set 'elem`, and putting a value into a Map is `Map.put : Map 'key val, 'key, val -> Map 'key val`.
* `Set` in Roc is like `Set` in Elm: it's shorthand for a `Map` with keys but no value, and it has a slightly different API.
> The main reason it's called `Map` instead of `Dict` is that it's annoying to have a conversation about `Dict` out loud, let alone to teach it in a workshop, because you have to be so careful to enunciate. `Map` is one letter shorter, doesn't have this problem, is widely used, and never seems to be confused with the `map` function in practice (in e.g. JavaScript and Rust, both of which have both `Map` and `map`) even though it seems like it would in theory.
Roc also has a special literal syntax for maps and sets. Here's how to write a `Map` literal:
Roc also has a literal syntax for maps and sets. Here's how to write a `Map` literal:
```elm
{{ "Sam" => 1, "Ali" => 2, firstName => 3 }}
{: "Sam" => 1, "Ali" => 2, firstName => 3 :}
```
This expression has the type `Map Str Int`, and the `firstName` variable would
@ -812,17 +797,18 @@ This works, but is not nearly as nice to read.
Additionally, map literals can compile direcly to efficient initialization code without needing to (hopefully be able to) optimize away the intermediate `List` involved in `fromList`.
`{{}}` is an empty `Map`.
`{::}` is an empty `Map`.
You can write a `Set` literal like this:
```elm
{[ "Sam", "Ali", firstName ]}
[: "Sam", "Ali", firstName :]
```
The `Set` literal syntax is partly for the initialization benefit, and also for symmetry
with the `Map` literal syntax.
`{[]}` is an empty `Set`.
`[::]` is an empty `Set`.
Roc does not have syntax for pattern matching on data structures - not even `[` `]` like Elm does.