Merge branch 'main' into luke-windows

This commit is contained in:
Brendan Hansknecht 2023-12-14 08:59:53 -08:00 committed by GitHub
commit 2955b00bb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1651 additions and 1067 deletions

View file

@ -538,20 +538,39 @@ update = \@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }, key
Err KeyNotFound ->
when alter Missing is
Present newValue ->
if maxBucketCapacity == 0 then
if List.len data >= (Num.toNat maxBucketCapacity) then
# Need to reallocate let regular insert handle that.
insert (@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }) key newValue
else
# Can skip work by jumping staight to the found bucket.
# That will be the location we want to insert in.
hash = hashKey key
distAndFingerprint = distAndFingerprintFromHash hash
baseDistAndFingerprint = distAndFingerprintFromHash hash
baseBucketIndex = bucketIndexFromHash hash shifts
insertHelper buckets data bucketIndex distAndFingerprint key newValue maxBucketCapacity maxLoadFactor shifts
# Due to the unrolling of loops in find along with loop optimizations,
# The bucketIndex is not guaranteed to be correct here.
# It is only correct if we have traversed past the number of find unrolls.
dist = circularDist baseBucketIndex bucketIndex (List.len buckets)
if dist <= findManualUnrolls then
insertHelper buckets data baseBucketIndex baseDistAndFingerprint key newValue maxBucketCapacity maxLoadFactor shifts
else
distAndFingerprint = incrementDistN baseDistAndFingerprint (Num.toU32 dist)
insertHelper buckets data bucketIndex distAndFingerprint key newValue maxBucketCapacity maxLoadFactor shifts
Missing ->
@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }
circularDist = \start, end, size ->
correction =
if start > end then
size
else
0
end
|> Num.subWrap start
|> Num.addWrap correction
## Returns the keys and values of a dictionary as a [List].
## This requires allocating a temporary list, prefer using [Dict.toList] or [Dict.walk] instead.
## ```
@ -709,21 +728,26 @@ maxBucketCount = maxSize
incrementDist = \distAndFingerprint ->
distAndFingerprint + distInc
incrementDistN = \distAndFingerprint, n ->
distAndFingerprint + (n * distInc)
decrementDist = \distAndFingerprint ->
distAndFingerprint - distInc
find : Dict k v, k -> { bucketIndex : Nat, result : Result v [KeyNotFound] }
find = \@Dict { buckets, data, shifts }, key ->
if !(List.isEmpty data) then
hash = hashKey key
distAndFingerprint = distAndFingerprintFromHash hash
bucketIndex = bucketIndexFromHash hash shifts
hash = hashKey key
distAndFingerprint = distAndFingerprintFromHash hash
bucketIndex = bucketIndexFromHash hash shifts
if !(List.isEmpty data) then
# TODO: this is true in the C++ code, confirm it in Roc as well.
# unrolled loop. *Always* check a few directly, then enter the loop. This is faster.
findFirstUnroll buckets bucketIndex distAndFingerprint data key
else
{ bucketIndex: 0, result: Err KeyNotFound }
{ bucketIndex, result: Err KeyNotFound }
findManualUnrolls = 2
findFirstUnroll : List Bucket, Nat, U32, List (k, v), k -> { bucketIndex : Nat, result : Result v [KeyNotFound] } where k implements Eq
findFirstUnroll = \buckets, bucketIndex, distAndFingerprint, data, key ->
@ -890,16 +914,16 @@ nextWhileLessHelper = \buckets, bucketIndex, distAndFingerprint ->
else
(bucketIndex, distAndFingerprint)
placeAndShiftUp = \buckets0, bucket0, bucketIndex ->
placeAndShiftUp = \buckets0, bucket, bucketIndex ->
loaded = listGetUnsafe buckets0 (Num.toNat bucketIndex)
if loaded.distAndFingerprint != 0 then
{ list: buckets1, value: bucket1 } = List.replace buckets0 (Num.toNat bucketIndex) bucket0
buckets1 = List.set buckets0 (Num.toNat bucketIndex) bucket
placeAndShiftUp
buckets1
{ bucket1 & distAndFingerprint: incrementDist bucket1.distAndFingerprint }
{ loaded & distAndFingerprint: incrementDist loaded.distAndFingerprint }
(nextBucketIndex bucketIndex (List.len buckets1))
else
List.set buckets0 (Num.toNat bucketIndex) bucket0
List.set buckets0 (Num.toNat bucketIndex) bucket
nextBucketIndex = \bucketIndex, maxBuckets ->
# I just ported this impl directly.
@ -1168,6 +1192,48 @@ expect
|> get 7
|> Bool.isEq (Ok "Testing")
# All BadKey's hash to the same location.
# This is needed to test some robinhood logic.
BadKey := U64 implements [
Eq,
Hash {
hash: hashBadKey,
},
]
hashBadKey : hasher, BadKey -> hasher where hasher implements Hasher
hashBadKey = \hasher, _ -> Hash.hash hasher 0
expect
badKeys = [
@BadKey 0,
@BadKey 1,
@BadKey 2,
@BadKey 3,
@BadKey 4,
@BadKey 5,
@BadKey 6,
@BadKey 5,
@BadKey 4,
@BadKey 3,
@BadKey 3,
@BadKey 3,
@BadKey 10,
]
dict =
acc, k <- List.walk badKeys (Dict.empty {})
Dict.update acc k \val ->
when val is
Present p -> Present (p + 1)
Missing -> Present 0
allInsertedCorrectly =
acc, k <- List.walk badKeys Bool.true
acc && Dict.contains dict k
allInsertedCorrectly
# Note, there are a number of places we should probably use set and replace unsafe.
# unsafe primitive that does not perform a bounds check
listGetUnsafe : List a, Nat -> a

View file

@ -560,8 +560,6 @@ tau = 2 * pi
# ------- Functions
## Convert a number to a [Str].
##
## This is the same as calling `Num.format {}` - so for more details on
## exact formatting, see `Num.format`.
## ```
## Num.toStr 42
## ```
@ -573,7 +571,6 @@ tau = 2 * pi
## When this function is given a non-[finite](Num.isFinite)
## [F64] or [F32] value, the returned string will be `"NaN"`, `"∞"`, or `"-∞"`.
##
## To get strings in hexadecimal, octal, or binary format, use `Num.format`.
toStr : Num * -> Str
intCast : Int a -> Int b

View file

@ -710,7 +710,7 @@ impl LlvmBackendMode {
match self {
LlvmBackendMode::Binary => false,
LlvmBackendMode::BinaryDev => false,
LlvmBackendMode::BinaryGlue => false,
LlvmBackendMode::BinaryGlue => true,
LlvmBackendMode::GenTest => true,
LlvmBackendMode::WasmGenTest => true,
LlvmBackendMode::CliTest => true,
@ -1060,9 +1060,9 @@ pub fn module_from_builtins<'ctx>(
// In testing, this adds about 20ms extra to compilation.
// Long term it would be best if we could do this on the zig side.
// This change enables us to dce all the parts of compiler-rt we don't use.
// That said, it would be better to dce them before roc app compiltation time.
// Anything not depended on by a `roc_builtin.` function could alread by DCE'd theoretically.
// The core issue is that we have to properly labael certain functions as private and DCE them.
// Otherwise, now that zig bundles all of compiler-rt, we would optimize and compile the entire library.
// Anything not depended on by a `roc_builtin.` function could already by DCE'd theoretically.
// That said, this workaround is good enough and fixes compilations times.
// Also, must_keep is the functions we depend on that would normally be provide by libc.
@ -1072,6 +1072,11 @@ pub fn module_from_builtins<'ctx>(
"floorf",
"memcpy",
"memset",
// I have no idea why this function is special.
// Without it, some tests hang on M1 mac outside of nix.
"__muloti4",
// fixes `Undefined Symbol in relocation`
"__udivti3",
// Roc special functions
"__roc_force_longjmp",
"__roc_force_setjmp",
@ -4463,31 +4468,68 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
}
}
let arguments_for_call = &arguments_for_call.into_bump_slice();
let call_result = if env.mode.returns_roc_result() {
debug_assert_eq!(args.len(), roc_function.get_params().len());
if args.len() == roc_function.get_params().len() {
let arguments_for_call = &arguments_for_call.into_bump_slice();
let dbg_loc = builder.get_current_debug_location().unwrap();
let roc_wrapper_function =
make_exception_catcher(env, layout_interner, roc_function, return_layout);
debug_assert_eq!(
arguments_for_call.len(),
roc_wrapper_function.get_params().len()
);
let dbg_loc = builder.get_current_debug_location().unwrap();
let roc_wrapper_function =
make_exception_catcher(env, layout_interner, roc_function, return_layout);
debug_assert_eq!(
arguments_for_call.len(),
roc_wrapper_function.get_params().len()
);
builder.position_at_end(entry);
builder.set_current_debug_location(dbg_loc);
builder.position_at_end(entry);
builder.set_current_debug_location(dbg_loc);
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
call_direct_roc_function(
env,
layout_interner,
roc_function,
wrapped_layout,
arguments_for_call,
)
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
call_direct_roc_function(
env,
layout_interner,
roc_function,
wrapped_layout,
arguments_for_call,
)
} else {
debug_assert_eq!(args.len() + 1, roc_function.get_params().len());
arguments_for_call.push(args[0]);
let arguments_for_call = &arguments_for_call.into_bump_slice();
let dbg_loc = builder.get_current_debug_location().unwrap();
let roc_wrapper_function =
make_exception_catcher(env, layout_interner, roc_function, return_layout);
builder.position_at_end(entry);
builder.set_current_debug_location(dbg_loc);
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
let call_result = call_direct_roc_function(
env,
layout_interner,
roc_wrapper_function,
wrapped_layout,
arguments_for_call,
);
let output_arg_index = 0;
let output_arg = c_function
.get_nth_param(output_arg_index as u32)
.unwrap()
.into_pointer_value();
env.builder.new_build_store(output_arg, call_result);
builder.new_build_return(None);
return c_function;
}
} else {
let arguments_for_call = &arguments_for_call.into_bump_slice();
call_direct_roc_function(
env,
layout_interner,
@ -4511,6 +4553,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
output_arg,
call_result,
);
builder.new_build_return(None);
c_function

View file

@ -80,10 +80,6 @@ pub(crate) fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
let i32_type = ctx.i32_type();
let void_type = ctx.void_type();
if let Some(func) = module.get_function("__muloti4") {
func.set_linkage(Linkage::WeakAny);
}
add_intrinsic(
ctx,
module,

View file

@ -684,6 +684,7 @@ impl MakeSpecializationsPass {
struct State<'a> {
pub root_id: ModuleId,
pub root_subs: Option<Subs>,
pub root_path: PathBuf,
pub cache_dir: PathBuf,
/// If the root is an app module, the shorthand specified in its header's `to` field
pub opt_platform_shorthand: Option<&'a str>,
@ -752,6 +753,7 @@ impl<'a> State<'a> {
fn new(
root_id: ModuleId,
root_path: PathBuf,
opt_platform_shorthand: Option<&'a str>,
target_info: TargetInfo,
function_kind: FunctionKind,
@ -770,6 +772,7 @@ impl<'a> State<'a> {
Self {
root_id,
root_path,
root_subs: None,
opt_platform_shorthand,
cache_dir,
@ -1077,8 +1080,9 @@ pub struct LoadStart<'a> {
arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: SharedIdentIdsByModule,
root_id: ModuleId,
opt_platform_shorthand: Option<&'a str>,
root_path: PathBuf,
root_msg: Msg<'a>,
opt_platform_shorthand: Option<&'a str>,
src_dir: PathBuf,
}
@ -1101,7 +1105,7 @@ impl<'a> LoadStart<'a> {
let res_loaded = load_filename(
arena,
filename,
filename.clone(),
true,
None,
None,
@ -1136,6 +1140,7 @@ impl<'a> LoadStart<'a> {
ident_ids_by_module,
src_dir,
root_id: header_output.module_id,
root_path: filename,
root_msg: header_output.msg,
opt_platform_shorthand: header_output.opt_platform_shorthand,
})
@ -1162,7 +1167,7 @@ impl<'a> LoadStart<'a> {
let header_output = load_from_str(
arena,
filename,
filename.clone(),
src,
Arc::clone(&arc_modules),
Arc::clone(&ident_ids_by_module),
@ -1178,6 +1183,7 @@ impl<'a> LoadStart<'a> {
src_dir,
ident_ids_by_module,
root_id,
root_path: filename,
root_msg,
opt_platform_shorthand: opt_platform_id,
})
@ -1352,6 +1358,7 @@ pub fn load_single_threaded<'a>(
arc_modules,
ident_ids_by_module,
root_id,
root_path,
root_msg,
src_dir,
opt_platform_shorthand,
@ -1367,6 +1374,7 @@ pub fn load_single_threaded<'a>(
let number_of_workers = 1;
let mut state = State::new(
root_id,
root_path,
opt_platform_shorthand,
target_info,
function_kind,
@ -1503,7 +1511,7 @@ fn state_thread_step<'a>(
Ok(ControlFlow::Break(LoadResult::Monomorphized(monomorphized)))
}
Msg::FailedToReadFile { filename, error } => {
let buf = to_file_problem_report_string(&filename, error);
let buf = to_file_problem_report_string(filename, error);
Err(LoadingProblem::FormattedReport(buf))
}
@ -1654,7 +1662,7 @@ pub fn report_loading_problem(
}
LoadingProblem::FormattedReport(report) => report,
LoadingProblem::FileProblem { filename, error } => {
to_file_problem_report_string(&filename, error)
to_file_problem_report_string(filename, error)
}
err => todo!("Loading error: {:?}", err),
}
@ -1677,6 +1685,7 @@ fn load_multi_threaded<'a>(
arc_modules,
ident_ids_by_module,
root_id,
root_path,
root_msg,
src_dir,
opt_platform_shorthand,
@ -1707,6 +1716,7 @@ fn load_multi_threaded<'a>(
let mut state = State::new(
root_id,
root_path,
opt_platform_shorthand,
target_info,
function_kind,
@ -2238,6 +2248,7 @@ fn update<'a>(
let buf = to_https_problem_report_string(
url,
Problem::InvalidUrl(url_err),
header.module_path,
);
return Err(LoadingProblem::FormattedReport(buf));
}
@ -3159,7 +3170,7 @@ fn finish_specialization<'a>(
}
Valid(To::NewPackage(p_or_p)) => PathBuf::from(p_or_p.as_str()),
other => {
let buf = to_missing_platform_report(state.root_id, other);
let buf = report_cannot_run(state.root_id, state.root_path, other);
return Err(LoadingProblem::FormattedReport(buf));
}
};
@ -3513,9 +3524,7 @@ fn load_builtin_module_help<'a>(
) -> (HeaderInfo<'a>, roc_parse::state::State<'a>) {
let is_root_module = false;
let opt_shorthand = None;
let filename = PathBuf::from(filename);
let parse_state = roc_parse::state::State::new(src_bytes.as_bytes());
let parsed = roc_parse::module::parse_header(arena, parse_state.clone());
@ -3968,6 +3977,7 @@ fn parse_header<'a>(
module_timing,
)?;
let filename = resolved_header.module_path.clone();
let mut messages = Vec::with_capacity(packages.len() + 1);
// It's important that the app header is first in the list!
@ -3982,6 +3992,7 @@ fn parse_header<'a>(
module_id,
module_ids,
ident_ids_by_module,
filename,
);
// Look at the app module's `to` keyword to determine which package was the platform.
@ -4084,6 +4095,7 @@ fn load_packages<'a>(
module_id: ModuleId,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: SharedIdentIdsByModule,
filename: PathBuf,
) {
// Load all the packages
for Loc { value: entry, .. } in packages.iter() {
@ -4121,7 +4133,7 @@ fn load_packages<'a>(
}
}
Err(problem) => {
let buf = to_https_problem_report_string(src, problem);
let buf = to_https_problem_report_string(src, problem, filename);
load_messages.push(Msg::FailedToLoad(LoadingProblem::FormattedReport(buf)));
return;
@ -6581,7 +6593,11 @@ fn to_parse_problem_report<'a>(
buf
}
fn to_missing_platform_report(module_id: ModuleId, other: &PlatformPath) -> String {
fn report_cannot_run(
module_id: ModuleId,
filename: PathBuf,
platform_path: &PlatformPath,
) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
use PlatformPath::*;
@ -6591,20 +6607,20 @@ fn to_missing_platform_report(module_id: ModuleId, other: &PlatformPath) -> Stri
let alloc = RocDocAllocator::new(&[], module_id, &interns);
let report = {
match other {
match platform_path {
Valid(_) => unreachable!(),
NotSpecified => {
let doc = alloc.stack([
alloc.reflow("I could not find a platform based on your input file."),
alloc.reflow(r"Does the module header contain an entry that looks like this:"),
alloc.reflow(r"Does the module header have an entry that looks like this?"),
alloc
.parser_suggestion(" packages { pf: \"platform\" }")
.parser_suggestion("packages { blah: \"…path or URL to platform…\" }")
.indent(4),
alloc.reflow("See also TODO."),
alloc.reflow("Tip: The following part of the tutorial has an example of specifying a platform:\n\n<https://www.roc-lang.org/tutorial#building-an-application>"),
]);
Report {
filename: "UNKNOWN.roc".into(),
filename,
doc,
title: "NO PLATFORM".to_string(),
severity: Severity::RuntimeError,
@ -6619,7 +6635,7 @@ fn to_missing_platform_report(module_id: ModuleId, other: &PlatformPath) -> Stri
]);
Report {
filename: "UNKNOWN.roc".into(),
filename,
doc,
title: "NO PLATFORM".to_string(),
severity: Severity::RuntimeError,
@ -6634,7 +6650,7 @@ fn to_missing_platform_report(module_id: ModuleId, other: &PlatformPath) -> Stri
]);
Report {
filename: "UNKNOWN.roc".into(),
filename,
doc,
title: "NO PLATFORM".to_string(),
severity: Severity::RuntimeError,
@ -6649,7 +6665,7 @@ fn to_missing_platform_report(module_id: ModuleId, other: &PlatformPath) -> Stri
]);
Report {
filename: "UNKNOWN.roc".into(),
filename,
doc,
title: "NO PLATFORM".to_string(),
severity: Severity::RuntimeError,

View file

@ -1,29 +1,29 @@
procedure Dict.1 (Dict.692):
let Dict.701 : List {U32, U32} = Array [];
let Dict.702 : List {[], []} = Array [];
let Dict.703 : U64 = 0i64;
let Dict.42 : Float32 = CallByName Dict.42;
let Dict.43 : U8 = CallByName Dict.43;
let Dict.700 : {List {U32, U32}, List {[], []}, U64, Float32, U8} = Struct {Dict.701, Dict.702, Dict.703, Dict.42, Dict.43};
ret Dict.700;
procedure Dict.1 (Dict.726):
let Dict.735 : List {U32, U32} = Array [];
let Dict.736 : List {[], []} = Array [];
let Dict.737 : U64 = 0i64;
let Dict.44 : Float32 = CallByName Dict.44;
let Dict.45 : U8 = CallByName Dict.45;
let Dict.734 : {List {U32, U32}, List {[], []}, U64, Float32, U8} = Struct {Dict.735, Dict.736, Dict.737, Dict.44, Dict.45};
ret Dict.734;
procedure Dict.4 (Dict.698):
let Dict.150 : List {[], []} = StructAtIndex 1 Dict.698;
let #Derived_gen.0 : List {U32, U32} = StructAtIndex 0 Dict.698;
procedure Dict.4 (Dict.732):
let Dict.157 : List {[], []} = StructAtIndex 1 Dict.732;
let #Derived_gen.0 : List {U32, U32} = StructAtIndex 0 Dict.732;
dec #Derived_gen.0;
let Dict.699 : U64 = CallByName List.6 Dict.150;
dec Dict.150;
ret Dict.699;
let Dict.733 : U64 = CallByName List.6 Dict.157;
dec Dict.157;
ret Dict.733;
procedure Dict.42 ():
let Dict.707 : Float32 = 0.8f64;
ret Dict.707;
procedure Dict.44 ():
let Dict.741 : Float32 = 0.8f64;
ret Dict.741;
procedure Dict.43 ():
let Dict.705 : U8 = 64i64;
let Dict.706 : U8 = 3i64;
let Dict.704 : U8 = CallByName Num.20 Dict.705 Dict.706;
ret Dict.704;
procedure Dict.45 ():
let Dict.739 : U8 = 64i64;
let Dict.740 : U8 = 3i64;
let Dict.738 : U8 = CallByName Num.20 Dict.739 Dict.740;
ret Dict.738;
procedure List.6 (#Attr.2):
let List.553 : U64 = lowlevel ListLen #Attr.2;

File diff suppressed because it is too large Load diff

View file

@ -234,7 +234,19 @@ fn verify_procedures<'a>(
if !has_changes.stdout.is_empty() {
println!("{}", std::str::from_utf8(&has_changes.stdout).unwrap());
panic!("Output changed: resolve conflicts and `git add` the file.");
panic!(indoc!(
r#"
Mono output has changed! This is normal when making changes to the builtins.
To fix it; run these commands locally:
cargo test -p test_mono -p uitest --no-fail-fast
git add -u
git commit -S -m "update mono tests"
git push origin YOUR_BRANCH_NAME
"#
));
}
}

View file

@ -3,22 +3,22 @@
app "test" provides [main] to "./platform"
f = \{} ->
#^{-1} <2918><117>{} -<120>[[f(1)]]-> <116>[Ok <2926>{}]<80>*
#^{-1} <2937><117>{} -<120>[[f(1)]]-> <116>[Ok <2945>{}]<80>*
when g {} is
# ^ <2908><2926>{} -<2916>[[g(2)]]-> <72>[Ok <2926>{}]<102>*
# ^ <2927><2945>{} -<2935>[[g(2)]]-> <72>[Ok <2945>{}]<102>*
_ -> Ok {}
g = \{} ->
#^{-1} <2908><2926>{} -<2916>[[g(2)]]-> <72>[Ok <2926>{}]<102>*
#^{-1} <2927><2945>{} -<2935>[[g(2)]]-> <72>[Ok <2945>{}]<102>*
when h {} is
# ^ <2913><2926>{} -<2921>[[h(3)]]-> <94>[Ok <2926>{}]<124>*
# ^ <2932><2945>{} -<2940>[[h(3)]]-> <94>[Ok <2945>{}]<124>*
_ -> Ok {}
h = \{} ->
#^{-1} <2913><2926>{} -<2921>[[h(3)]]-> <94>[Ok <2926>{}]<124>*
#^{-1} <2932><2945>{} -<2940>[[h(3)]]-> <94>[Ok <2945>{}]<124>*
when f {} is
# ^ <2918><117>{} -<120>[[f(1)]]-> <116>[Ok <2926>{}]<80>*
# ^ <2937><117>{} -<120>[[f(1)]]-> <116>[Ok <2945>{}]<80>*
_ -> Ok {}
main = f {}
# ^ <2928><133>{} -<136>[[f(1)]]-> <138>[Ok <2926>{}]<2927>w_a
# ^ <2947><133>{} -<136>[[f(1)]]-> <138>[Ok <2945>{}]<2946>w_a