Merge remote-tracking branch 'remote/main' into remove-json

This commit is contained in:
Luke Boswell 2023-06-07 15:38:01 +10:00
commit 092676c4b7
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
92 changed files with 2039 additions and 417 deletions

11
Cargo.lock generated
View file

@ -358,6 +358,16 @@ version = "3.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
[[package]]
name = "byte-unit"
version = "4.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c"
dependencies = [
"serde",
"utf8-width",
]
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.13.1" version = "1.13.1"
@ -3850,6 +3860,7 @@ name = "roc_reporting"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"byte-unit",
"distance", "distance",
"indoc", "indoc",
"insta", "insta",

View file

@ -121,16 +121,18 @@ impl<'a> Env<'a> {
Ok(symbol) Ok(symbol)
} }
None => Err(RuntimeError::LookupNotInScope( None => Err(RuntimeError::LookupNotInScope {
Loc { loc_name: Loc {
value: Ident::from(ident), value: Ident::from(ident),
region, region,
}, },
self.ident_ids suggestion_options: self
.ident_ids
.ident_strs() .ident_strs()
.map(|(_, string)| string.into()) .map(|(_, string)| string.into())
.collect(), .collect(),
)), underscored_suggestion_region: None,
}),
} }
} else { } else {
match self.dep_idents.get(&module_id) { match self.dep_idents.get(&module_id) {

View file

@ -203,13 +203,14 @@ impl Scope {
pub fn lookup(&mut self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> { pub fn lookup(&mut self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
match self.idents.get(ident) { match self.idents.get(ident) {
Some((symbol, _)) => Ok(*symbol), Some((symbol, _)) => Ok(*symbol),
None => Err(RuntimeError::LookupNotInScope( None => Err(RuntimeError::LookupNotInScope {
Loc { loc_name: Loc {
region, region,
value: ident.clone().into(), value: ident.clone().into(),
}, },
self.idents.keys().map(|v| v.as_ref().into()).collect(), suggestion_options: self.idents.keys().map(|v| v.as_ref().into()).collect(),
)), underscored_suggestion_region: None,
}),
} }
} }

View file

@ -48,10 +48,6 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
free(@alignCast(16, @ptrCast([*]u8, c_ptr))); free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -47,10 +47,6 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
free(@alignCast(16, @ptrCast([*]u8, c_ptr))); free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -48,10 +48,6 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
free(@alignCast(16, @ptrCast([*]u8, c_ptr))); free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -73,10 +73,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -72,10 +72,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -77,10 +77,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -67,10 +67,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -34,7 +34,9 @@ pub const DEFAULT_ROC_FILENAME: &str = "main.roc";
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct CodeGenTiming { pub struct CodeGenTiming {
pub code_gen: Duration, pub generate_final_ir: Duration,
pub code_gen_object: Duration,
pub total: Duration,
} }
pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> Problems { pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> Problems {
@ -144,7 +146,7 @@ fn gen_from_mono_module_llvm<'a>(
use inkwell::module::Linkage; use inkwell::module::Linkage;
use inkwell::targets::{FileType, RelocMode}; use inkwell::targets::{FileType, RelocMode};
let code_gen_start = Instant::now(); let all_code_gen_start = Instant::now();
// Generate the binary // Generate the binary
let target_info = roc_target::TargetInfo::from(target); let target_info = roc_target::TargetInfo::from(target);
@ -238,6 +240,10 @@ fn gen_from_mono_module_llvm<'a>(
&loaded.glue_layouts, &loaded.glue_layouts,
); );
// We are now finished building the LLVM IR.
let generate_final_ir = all_code_gen_start.elapsed();
let code_gen_object_start = Instant::now();
env.dibuilder.finalize(); env.dibuilder.finalize();
// we don't use the debug info, and it causes weird errors. // we don't use the debug info, and it causes weird errors.
@ -439,11 +445,16 @@ fn gen_from_mono_module_llvm<'a>(
} }
}; };
let code_gen = code_gen_start.elapsed(); let code_gen_object = code_gen_object_start.elapsed();
let total = all_code_gen_start.elapsed();
( (
CodeObject::MemoryBuffer(memory_buffer), CodeObject::MemoryBuffer(memory_buffer),
CodeGenTiming { code_gen }, CodeGenTiming {
generate_final_ir,
code_gen_object,
total,
},
ExpectMetadata { ExpectMetadata {
interns: env.interns, interns: env.interns,
layout_interner: loaded.layout_interner, layout_interner: loaded.layout_interner,
@ -503,7 +514,7 @@ fn gen_from_mono_module_dev_wasm32<'a>(
preprocessed_host_path: &Path, preprocessed_host_path: &Path,
wasm_dev_stack_bytes: Option<u32>, wasm_dev_stack_bytes: Option<u32>,
) -> GenFromMono<'a> { ) -> GenFromMono<'a> {
let code_gen_start = Instant::now(); let all_code_gen_start = Instant::now();
let MonomorphizedModule { let MonomorphizedModule {
module_id, module_id,
procedures, procedures,
@ -550,11 +561,18 @@ fn gen_from_mono_module_dev_wasm32<'a>(
procedures, procedures,
); );
let code_gen = code_gen_start.elapsed(); let generate_final_ir = all_code_gen_start.elapsed();
let code_gen_object_start = Instant::now();
let code_gen_object = code_gen_object_start.elapsed();
let total = all_code_gen_start.elapsed();
( (
CodeObject::Vector(final_binary_bytes), CodeObject::Vector(final_binary_bytes),
CodeGenTiming { code_gen }, CodeGenTiming {
generate_final_ir,
code_gen_object,
total,
},
ExpectMetadata { ExpectMetadata {
interns, interns,
layout_interner, layout_interner,
@ -569,7 +587,7 @@ fn gen_from_mono_module_dev_assembly<'a>(
target: &target_lexicon::Triple, target: &target_lexicon::Triple,
backend_mode: AssemblyBackendMode, backend_mode: AssemblyBackendMode,
) -> GenFromMono<'a> { ) -> GenFromMono<'a> {
let code_gen_start = Instant::now(); let all_code_gen_start = Instant::now();
let lazy_literals = true; let lazy_literals = true;
@ -593,15 +611,23 @@ fn gen_from_mono_module_dev_assembly<'a>(
let module_object = let module_object =
roc_gen_dev::build_module(&env, &mut interns, &mut layout_interner, target, procedures); roc_gen_dev::build_module(&env, &mut interns, &mut layout_interner, target, procedures);
let code_gen = code_gen_start.elapsed(); let generate_final_ir = all_code_gen_start.elapsed();
let code_gen_object_start = Instant::now();
let module_out = module_object let module_out = module_object
.write() .write()
.expect("failed to build output object"); .expect("failed to build output object");
let code_gen_object = code_gen_object_start.elapsed();
let total = all_code_gen_start.elapsed();
( (
CodeObject::Vector(module_out), CodeObject::Vector(module_out),
CodeGenTiming { code_gen }, CodeGenTiming {
generate_final_ir,
code_gen_object,
total,
},
ExpectMetadata { ExpectMetadata {
interns, interns,
layout_interner, layout_interner,
@ -919,9 +945,12 @@ fn build_loaded_file<'a>(
report_timing( report_timing(
buf, buf,
"Generate Assembly from Mono IR", "Generate final IR from Mono IR",
code_gen_timing.code_gen, code_gen_timing.generate_final_ir,
); );
report_timing(buf, "Generate object", code_gen_timing.code_gen_object);
buf.push('\n');
report_timing(buf, "Total", code_gen_timing.total);
let compilation_end = compilation_start.elapsed(); let compilation_end = compilation_start.elapsed();
let size = roc_app_bytes.len(); let size = roc_app_bytes.len();

View file

@ -0,0 +1,80 @@
const std = @import("std");
const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const musl = @import("libc/musl.zig");
const folly = @import("libc/folly.zig");
const cpuid = @import("libc/cpuid.zig");
comptime {
// TODO: remove this workaround.
// Our wasm llvm pipeline always links in memcpy.
// As such, our impl will conflict.
if (arch != .wasm32) {
@export(memcpy, .{ .name = "memcpy", .linkage = .Strong });
}
}
const Memcpy = fn (noalias [*]u8, noalias [*]const u8, len: usize) callconv(.C) [*]u8;
pub var memcpy_target: Memcpy = switch (arch) {
.x86_64 => dispatch_memcpy,
else => unreachable,
};
pub fn memcpy(noalias dest: [*]u8, noalias src: [*]const u8, len: usize) callconv(.C) [*]u8 {
switch (arch) {
// x86_64 has a special optimized memcpy that can use avx2.
.x86_64 => {
return memcpy_target(dest, src, len);
},
else => {
return musl.memcpy(dest, src, len);
},
}
}
const MemcpyDecision = enum {
uninitialized,
folly_prefetchw,
folly_prefetcht0,
musl,
};
var memcpy_decision: MemcpyDecision = .uninitialized;
fn dispatch_memcpy(noalias dest: [*]u8, noalias src: [*]const u8, len: usize) callconv(.C) [*]u8 {
switch (arch) {
.x86_64 => {
// TODO: Switch this to overwrite the memcpy_target pointer once the surgical linker can support it.
// Then dispatch will just happen on the first call instead of every call.
// if (cpuid.supports_avx2()) {
// if (cpuid.supports_prefetchw()) {
// memcpy_target = folly.memcpy_prefetchw;
// } else {
// memcpy_target = folly.memcpy_prefetcht0;
// }
// } else {
// memcpy_target = musl.memcpy;
// }
// return memcpy_target(dest, src, len);
switch (memcpy_decision) {
.uninitialized => {
if (cpuid.supports_avx2()) {
if (cpuid.supports_prefetchw()) {
memcpy_decision = .folly_prefetchw;
} else {
memcpy_decision = .folly_prefetcht0;
}
} else {
memcpy_decision = .musl;
}
return dispatch_memcpy(dest, src, len);
},
.folly_prefetchw => return folly.memcpy_prefetchw(dest, src, len),
.folly_prefetcht0 => return folly.memcpy_prefetcht0(dest, src, len),
.musl => return musl.memcpy(dest, src, len),
}
},
else => unreachable,
}
}

View file

@ -0,0 +1,7 @@
const builtin = @import("builtin");
const os = builtin.os;
pub const function_prefix = switch (os.tag) {
.macos => "_",
else => "",
};

View file

@ -0,0 +1,53 @@
// Check if AVX2 is supported.
// Returns 1 if AVX2 is supported, 0 otherwise.
.global {[function_prefix]s}supports_avx2;
{[function_prefix]s}supports_avx2:
// Save the EBX register.
push %rbx
// Call the CPUID instruction with the EAX register set to 7 and ECX set to 0.
// This will get the CPUID information for the current CPU.
mov $7, %eax
mov $0, %ecx
cpuid
// The AVX2 feature flag is located in the EBX register at bit 5.
bt $5, %ebx
jc .avx2_supported
// AVX2 is not supported.
pop %rbx
mov $0, %eax
ret
.avx2_supported:
pop %rbx
mov $1, %eax
ret
// Check if prefetchw is supported.
// Returns 1 if the prefetchw instruction is supported, 0 otherwise.
.global {[function_prefix]s}supports_prefetchw;
{[function_prefix]s}supports_prefetchw:
// Save the EBX register.
push %rbx
// Call the CPUID instruction with the EAX register set to 0x80000001 and ECX set to 0.
// This will get the CPUID information for the current CPU.
mov $0x80000001, %eax
mov $0, %ecx
cpuid
// The prefetchw feature flag is located in the ECX register at bit 8.
bt $8, %ecx
jc .prefetchw_supported
// AVX2 is not supported.
pop %rbx
mov $0, %eax
ret
.prefetchw_supported:
pop %rbx
mov $1, %eax
ret

View file

@ -0,0 +1,18 @@
const std = @import("std");
const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const function_prefix = @import("assembly_util.zig").function_prefix;
// I couldn't manage to define this in a PIE friendly way with inline assembly.
// Instead, I am defining it as global assembly functions.
comptime {
switch (arch) {
.x86_64 => {
asm (std.fmt.comptimePrint(@embedFile("cpuid.S"), .{ .function_prefix = function_prefix }));
},
else => unreachable,
}
}
pub extern fn supports_avx2() bool;
pub extern fn supports_prefetchw() bool;

View file

@ -0,0 +1,2 @@
pub const memcpy_prefetchw = @import("folly/memcpy.zig").__folly_memcpy_prefetchw;
pub const memcpy_prefetcht0 = @import("folly/memcpy.zig").__folly_memcpy_prefetcht0;

View file

@ -0,0 +1,437 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* __folly_memcpy: An optimized memcpy implementation that uses prefetch and
* AVX2 instructions.
*
* This implementation of memcpy acts as a memmove: while overlapping copies
* are undefined in memcpy, in some implementations they're the same function and
* legacy programs rely on this behavior.
*
* This implementation uses prefetch to avoid dtlb misses. This can
* substantially reduce dtlb store misses in cases where the destination
* location is absent from L1 cache and where the copy size is small enough
* that the hardware prefetcher doesn't have a large impact.
*
* The number of branches is limited by the use of overlapping loads & stores.
* This helps with copies where the source and destination cache lines are already
* present in L1 because there are fewer instructions to execute and fewer
* branches to potentially mispredict.
* e.g. to copy the last 4 <= n <= 7 bytes: copy the first & last 4 bytes (overlapped):
* movl (%rsi), %r8d
* movl -4(%rsi,%rdx), %r9d
* movl %r8d, (%rdi)
* movl %r9d, -4(%rdi,%rdx)
*
*
* For sizes up to 256 all source data is first read into registers and then written:
* - n <= 16: overlapping movs
* - n <= 32: overlapping unaligned 16-byte SSE XMM load/stores
* - n <= 256: overlapping unaligned 32-byte AVX YMM load/stores
*
* Large copies (> 256 bytes) use unaligned loads + aligned stores.
* This is observed to always be faster than rep movsb, so the rep movsb
* instruction is not used.
* - The head & tail may be unaligned => they're always written using unaligned stores.
*
* If the copy size is humongous (> 32 KiB) and the source and destination are both
* aligned, this memcpy will use non-temporal operations (AVX2). This can have
* a substantial speedup for copies where data is absent from L1, but it
* is significantly slower if the source and destination data were already
* in L1. The use of non-temporal operations also has the effect that after
* the copy is complete, the data will be moved out of L1, even if the data was
* present before the copy started.
*
* For n > 256 and overlapping src & dst buffers (memmove):
* - use unaligned loads + aligned stores, but not non-temporal stores
* - for dst < src forward copy in 128 byte batches:
* - unaligned load the first 32 bytes & last 4 x 32 bytes
* - forward copy (unaligned load + aligned stores) 4 x 32 bytes at a time
* - unaligned store the first 32 bytes & last 4 x 32 bytes
* - for dst > src backward copy in 128 byte batches:
* - unaligned load the first 4 x 32 bytes & last 32 bytes
* - backward copy (unaligned load + aligned stores) 4 x 32 bytes at a time
* - unaligned store the first 4 x 32 bytes & last 32 bytes
*
* @author Logan Evans <lpe@fb.com>
*/
// .type {[function_prefix]s}__folly_memcpy_short_{[prefetch]s}, @function not supported by windows
{[function_prefix]s}__folly_memcpy_short_{[prefetch]s}:
.cfi_startproc
.L_GE1_LE7_{[prefetch]s}:
cmp $1, %rdx
je .L_EQ1_{[prefetch]s}
cmp $4, %rdx
jae .L_GE4_LE7_{[prefetch]s}
.L_GE2_LE3_{[prefetch]s}:
movw (%rsi), %r8w
movw -2(%rsi,%rdx), %r9w
movw %r8w, (%rdi)
movw %r9w, -2(%rdi,%rdx)
ret
.balign 2
.L_EQ1_{[prefetch]s}:
movb (%rsi), %r8b
movb %r8b, (%rdi)
ret
// Aligning the target of a jump to an even address has a measurable
// speedup in microbenchmarks.
.balign 2
.L_GE4_LE7_{[prefetch]s}:
movl (%rsi), %r8d
movl -4(%rsi,%rdx), %r9d
movl %r8d, (%rdi)
movl %r9d, -4(%rdi,%rdx)
ret
.cfi_endproc
// .size {[function_prefix]s}__folly_memcpy_short_{[prefetch]s}, .-{[function_prefix]s}__folly_memcpy_short_{[prefetch]s} not supported by windows
// memcpy is an alternative entrypoint into the function named __folly_memcpy.
// The compiler is able to call memcpy since the name is global while
// stacktraces will show __folly_memcpy since that is the name of the function.
// This is intended to aid in debugging by making it obvious which version of
// memcpy is being used.
.balign 64
.globl {[function_prefix]s}__folly_memcpy_{[prefetch]s}
// .type {[function_prefix]s}__folly_memcpy_{[prefetch]s}, @function not supported by windows
{[function_prefix]s}__folly_memcpy_{[prefetch]s}:
.cfi_startproc
mov %rdi, %rax // return: $rdi
test %rdx, %rdx
je .L_EQ0_{[prefetch]s}
{[prefetch]s} (%rdi)
{[prefetch]s} -1(%rdi,%rdx)
cmp $8, %rdx
jb .L_GE1_LE7_{[prefetch]s}
.L_GE8_{[prefetch]s}:
cmp $32, %rdx
ja .L_GE33_{[prefetch]s}
.L_GE8_LE32_{[prefetch]s}:
cmp $16, %rdx
ja .L_GE17_LE32_{[prefetch]s}
.L_GE8_LE16_{[prefetch]s}:
mov (%rsi), %r8
mov -8(%rsi,%rdx), %r9
mov %r8, (%rdi)
mov %r9, -8(%rdi,%rdx)
.L_EQ0_{[prefetch]s}:
ret
.balign 2
.L_GE17_LE32_{[prefetch]s}:
movdqu (%rsi), %xmm0
movdqu -16(%rsi,%rdx), %xmm1
movdqu %xmm0, (%rdi)
movdqu %xmm1, -16(%rdi,%rdx)
ret
.balign 2
.L_GE193_LE256_{[prefetch]s}:
vmovdqu %ymm3, 96(%rdi)
vmovdqu %ymm4, -128(%rdi,%rdx)
.L_GE129_LE192_{[prefetch]s}:
vmovdqu %ymm2, 64(%rdi)
vmovdqu %ymm5, -96(%rdi,%rdx)
.L_GE65_LE128_{[prefetch]s}:
vmovdqu %ymm1, 32(%rdi)
vmovdqu %ymm6, -64(%rdi,%rdx)
.L_GE33_LE64_{[prefetch]s}:
vmovdqu %ymm0, (%rdi)
vmovdqu %ymm7, -32(%rdi,%rdx)
vzeroupper
ret
.balign 2
.L_GE33_{[prefetch]s}:
vmovdqu (%rsi), %ymm0
vmovdqu -32(%rsi,%rdx), %ymm7
cmp $64, %rdx
jbe .L_GE33_LE64_{[prefetch]s}
{[prefetch]s} 64(%rdi)
vmovdqu 32(%rsi), %ymm1
vmovdqu -64(%rsi,%rdx), %ymm6
cmp $128, %rdx
jbe .L_GE65_LE128_{[prefetch]s}
{[prefetch]s} 128(%rdi)
vmovdqu 64(%rsi), %ymm2
vmovdqu -96(%rsi,%rdx), %ymm5
cmp $192, %rdx
jbe .L_GE129_LE192_{[prefetch]s}
{[prefetch]s} 192(%rdi)
vmovdqu 96(%rsi), %ymm3
vmovdqu -128(%rsi,%rdx), %ymm4
cmp $256, %rdx
jbe .L_GE193_LE256_{[prefetch]s}
.L_GE257_{[prefetch]s}:
{[prefetch]s} 256(%rdi)
// Check if there is an overlap. If there is an overlap then the caller
// has a bug since this is undefined behavior. However, for legacy
// reasons this behavior is expected by some callers.
//
// All copies through 256 bytes will operate as a memmove since for
// those sizes all reads are performed before any writes.
//
// This check uses the idea that there is an overlap if
// (%rdi < (%rsi + %rdx)) && (%rsi < (%rdi + %rdx)),
// or equivalently, there is no overlap if
// ((%rsi + %rdx) <= %rdi) || ((%rdi + %rdx) <= %rsi).
//
// %r9 will be used after .L_ALIGNED_DST_LOOP to calculate how many
// bytes remain to be copied.
// (%rsi + %rdx <= %rdi) => no overlap
lea (%rsi,%rdx), %r9
cmp %rdi, %r9
jbe .L_NO_OVERLAP_{[prefetch]s}
// (%rdi + %rdx <= %rsi) => no overlap
lea (%rdi,%rdx), %r8
cmp %rsi, %r8
// If no info is available in branch predictor's cache, Intel CPUs assume
// forward jumps are not taken. Use a forward jump as overlapping buffers
// are unlikely.
ja .L_OVERLAP_{[prefetch]s}
.balign 2
.L_NO_OVERLAP_{[prefetch]s}:
vmovdqu %ymm0, (%rdi)
vmovdqu %ymm1, 32(%rdi)
vmovdqu %ymm2, 64(%rdi)
vmovdqu %ymm3, 96(%rdi)
// Align %rdi to a 32 byte boundary.
// %rcx = 128 - 31 & %rdi
mov $128, %rcx
and $31, %rdi
sub %rdi, %rcx
lea (%rsi,%rcx), %rsi
lea (%rax,%rcx), %rdi
sub %rcx, %rdx
// %r8 is the end condition for the loop.
lea -128(%rsi,%rdx), %r8
// This threshold is half of L1 cache on a Skylake machine, which means that
// potentially all of L1 will be populated by this copy once it is executed
// (dst and src are cached for temporal copies).
// NON_TEMPORAL_STORE_THRESHOLD = $32768
// cmp NON_TEMPORAL_STORE_THRESHOLD, %rdx
cmp $32768, %rdx
jae .L_NON_TEMPORAL_LOOP_{[prefetch]s}
.balign 2
.L_ALIGNED_DST_LOOP_{[prefetch]s}:
{[prefetch]s} 128(%rdi)
{[prefetch]s} 192(%rdi)
vmovdqu (%rsi), %ymm0
vmovdqu 32(%rsi), %ymm1
vmovdqu 64(%rsi), %ymm2
vmovdqu 96(%rsi), %ymm3
add $128, %rsi
vmovdqa %ymm0, (%rdi)
vmovdqa %ymm1, 32(%rdi)
vmovdqa %ymm2, 64(%rdi)
vmovdqa %ymm3, 96(%rdi)
add $128, %rdi
cmp %r8, %rsi
jb .L_ALIGNED_DST_LOOP_{[prefetch]s}
.L_ALIGNED_DST_LOOP_END_{[prefetch]s}:
sub %rsi, %r9
mov %r9, %rdx
vmovdqu %ymm4, -128(%rdi,%rdx)
vmovdqu %ymm5, -96(%rdi,%rdx)
vmovdqu %ymm6, -64(%rdi,%rdx)
vmovdqu %ymm7, -32(%rdi,%rdx)
vzeroupper
ret
.balign 2
.L_NON_TEMPORAL_LOOP_{[prefetch]s}:
testb $31, %sil
jne .L_ALIGNED_DST_LOOP_{[prefetch]s}
// This is prefetching the source data unlike ALIGNED_DST_LOOP which
// prefetches the destination data. This choice is again informed by
// benchmarks. With a non-temporal store the entirety of the cache line
// is being written so the previous data can be discarded without being
// fetched.
prefetchnta 128(%rsi)
prefetchnta 196(%rsi)
vmovntdqa (%rsi), %ymm0
vmovntdqa 32(%rsi), %ymm1
vmovntdqa 64(%rsi), %ymm2
vmovntdqa 96(%rsi), %ymm3
add $128, %rsi
vmovntdq %ymm0, (%rdi)
vmovntdq %ymm1, 32(%rdi)
vmovntdq %ymm2, 64(%rdi)
vmovntdq %ymm3, 96(%rdi)
add $128, %rdi
cmp %r8, %rsi
jb .L_NON_TEMPORAL_LOOP_{[prefetch]s}
sfence
jmp .L_ALIGNED_DST_LOOP_END_{[prefetch]s}
.L_OVERLAP_{[prefetch]s}:
.balign 2
cmp %rdi, %rsi
jb .L_OVERLAP_BWD_{[prefetch]s} // %rsi < %rdi => backward-copy
je .L_RET_{[prefetch]s} // %rsi == %rdi => return, nothing to copy
// Source & destination buffers overlap. Forward copy.
vmovdqu (%rsi), %ymm8
// Align %rdi to a 32 byte boundary.
// %rcx = 32 - 31 & %rdi
mov $32, %rcx
and $31, %rdi
sub %rdi, %rcx
lea (%rsi,%rcx), %rsi
lea (%rax,%rcx), %rdi
sub %rcx, %rdx
// %r8 is the end condition for the loop.
lea -128(%rsi,%rdx), %r8
.L_OVERLAP_FWD_ALIGNED_DST_LOOP_{[prefetch]s}:
{[prefetch]s} 128(%rdi)
{[prefetch]s} 192(%rdi)
vmovdqu (%rsi), %ymm0
vmovdqu 32(%rsi), %ymm1
vmovdqu 64(%rsi), %ymm2
vmovdqu 96(%rsi), %ymm3
add $128, %rsi
vmovdqa %ymm0, (%rdi)
vmovdqa %ymm1, 32(%rdi)
vmovdqa %ymm2, 64(%rdi)
vmovdqa %ymm3, 96(%rdi)
add $128, %rdi
cmp %r8, %rsi
jb .L_OVERLAP_FWD_ALIGNED_DST_LOOP_{[prefetch]s}
sub %rsi, %r9
mov %r9, %rdx
vmovdqu %ymm4, -128(%rdi,%rdx)
vmovdqu %ymm5, -96(%rdi,%rdx)
vmovdqu %ymm6, -64(%rdi,%rdx)
vmovdqu %ymm7, -32(%rdi,%rdx)
vmovdqu %ymm8, (%rax) // %rax == the original (unaligned) %rdi
vzeroupper
.L_RET_{[prefetch]s}:
ret
.L_OVERLAP_BWD_{[prefetch]s}:
// Save last 32 bytes.
vmovdqu -32(%rsi, %rdx), %ymm8
lea -32(%rdi, %rdx), %r9
// %r8 is the end condition for the loop.
lea 128(%rsi), %r8
// Align %rdi+%rdx (destination end) to a 32 byte boundary.
// %rcx = (%rdi + %rdx - 32) & 31
mov %r9, %rcx
and $31, %rcx
// Set %rsi & %rdi to the end of the 32 byte aligned range.
sub %rcx, %rdx
add %rdx, %rsi
add %rdx, %rdi
.L_OVERLAP_BWD_ALIGNED_DST_LOOP_{[prefetch]s}:
{[prefetch]s} -128(%rdi)
{[prefetch]s} -192(%rdi)
vmovdqu -32(%rsi), %ymm4
vmovdqu -64(%rsi), %ymm5
vmovdqu -96(%rsi), %ymm6
vmovdqu -128(%rsi), %ymm7
sub $128, %rsi
vmovdqa %ymm4, -32(%rdi)
vmovdqa %ymm5, -64(%rdi)
vmovdqa %ymm6, -96(%rdi)
vmovdqa %ymm7, -128(%rdi)
sub $128, %rdi
cmp %r8, %rsi
ja .L_OVERLAP_BWD_ALIGNED_DST_LOOP_{[prefetch]s}
vmovdqu %ymm0, (%rax) // %rax == the original unaligned %rdi
vmovdqu %ymm1, 32(%rax)
vmovdqu %ymm2, 64(%rax)
vmovdqu %ymm3, 96(%rax)
vmovdqu %ymm8, (%r9)
vzeroupper
ret
.cfi_endproc
// .size {[function_prefix]s}__folly_memcpy_{[prefetch]s}, .-{[function_prefix]s}__folly_memcpy_{[prefetch]s} not supported by windows

View file

@ -0,0 +1,18 @@
const std = @import("std");
const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const function_prefix = @import("../assembly_util.zig").function_prefix;
comptime {
switch (arch) {
.x86_64 => {
inline for ([_][]const u8{ "prefetchw", "prefetcht0" }) |prefetch| {
asm (std.fmt.comptimePrint(@embedFile("memcpy-x86_64.S"), .{ .prefetch = prefetch, .function_prefix = function_prefix }));
}
},
else => unreachable,
}
}
pub extern fn __folly_memcpy_prefetchw(noalias dest: [*]u8, noalias src: [*]const u8, len: usize) callconv(.C) [*]u8;
pub extern fn __folly_memcpy_prefetcht0(noalias dest: [*]u8, noalias src: [*]const u8, len: usize) callconv(.C) [*]u8;

View file

@ -0,0 +1 @@
pub const memcpy = @import("musl/memcpy.zig").memcpy;

View file

@ -0,0 +1,193 @@
musl as a whole is licensed under the following standard MIT license:
----------------------------------------------------------------------
Copyright © 2005-2020 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------
Authors/contributors include:
A. Wilcox
Ada Worcester
Alex Dowad
Alex Suykov
Alexander Monakov
Andre McCurdy
Andrew Kelley
Anthony G. Basile
Aric Belsito
Arvid Picciani
Bartosz Brachaczek
Benjamin Peterson
Bobby Bingham
Boris Brezillon
Brent Cook
Chris Spiegel
Clément Vasseur
Daniel Micay
Daniel Sabogal
Daurnimator
David Carlier
David Edelsohn
Denys Vlasenko
Dmitry Ivanov
Dmitry V. Levin
Drew DeVault
Emil Renner Berthing
Fangrui Song
Felix Fietkau
Felix Janda
Gianluca Anzolin
Hauke Mehrtens
He X
Hiltjo Posthuma
Isaac Dunham
Jaydeep Patil
Jens Gustedt
Jeremy Huntwork
Jo-Philipp Wich
Joakim Sindholt
John Spencer
Julien Ramseier
Justin Cormack
Kaarle Ritvanen
Khem Raj
Kylie McClain
Leah Neukirchen
Luca Barbato
Luka Perkov
M Farkas-Dyck (Strake)
Mahesh Bodapati
Markus Wichmann
Masanori Ogino
Michael Clark
Michael Forney
Mikhail Kremnyov
Natanael Copa
Nicholas J. Kain
orc
Pascal Cuoq
Patrick Oppenlander
Petr Hosek
Petr Skocik
Pierre Carrier
Reini Urban
Rich Felker
Richard Pennington
Ryan Fairfax
Samuel Holland
Segev Finer
Shiz
sin
Solar Designer
Stefan Kristiansson
Stefan O'Rear
Szabolcs Nagy
Timo Teräs
Trutz Behn
Valentin Ochs
Will Dietz
William Haddon
William Pitcock
Portions of this software are derived from third-party works licensed
under terms compatible with the above MIT license:
The TRE regular expression implementation (src/regex/reg* and
src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
under a 2-clause BSD license (license text in the source files). The
included version has been heavily modified by Rich Felker in 2012, in
the interests of size, simplicity, and namespace cleanliness.
Much of the math library code (src/math/* and src/complex/*) is
Copyright © 1993,2004 Sun Microsystems or
Copyright © 2003-2011 David Schultz or
Copyright © 2003-2009 Steven G. Kargl or
Copyright © 2003-2009 Bruce D. Evans or
Copyright © 2008 Stephen L. Moshier or
Copyright © 2017-2018 Arm Limited
and labelled as such in comments in the individual source files. All
have been licensed under extremely permissive terms.
The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008
The Android Open Source Project and is licensed under a two-clause BSD
license. It was taken from Bionic libc, used on Android.
The AArch64 memcpy and memset code (src/string/aarch64/*) are
Copyright © 1999-2019, Arm Limited.
The implementation of DES for crypt (src/crypt/crypt_des.c) is
Copyright © 1994 David Burren. It is licensed under a BSD license.
The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was
originally written by Solar Designer and placed into the public
domain. The code also comes with a fallback permissive license for use
in jurisdictions that may not recognize the public domain.
The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
Valentin Ochs and is licensed under an MIT-style license.
The x86_64 port was written by Nicholas J. Kain and is licensed under
the standard MIT terms.
The mips and microblaze ports were originally written by Richard
Pennington for use in the ellcc project. The original code was adapted
by Rich Felker for build system and code conventions during upstream
integration. It is licensed under the standard MIT terms.
The mips64 port was contributed by Imagination Technologies and is
licensed under the standard MIT terms.
The powerpc port was also originally written by Richard Pennington,
and later supplemented and integrated by John Spencer. It is licensed
under the standard MIT terms.
All other files which have no copyright comments are original works
produced specifically for use as part of this library, written either
by Rich Felker, the main author of the library, or by one or more
contibutors listed above. Details on authorship of individual files
can be found in the git version control history of the project. The
omission of copyright and license comments in each file is in the
interest of source tree size.
In addition, permission is hereby granted for all public header files
(include/* and arch/*/bits/*) and crt files intended to be linked into
applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
the copyright notice and permission notice otherwise required by the
license, and to use these files without any requirement of
attribution. These files include substantial contributions from:
Bobby Bingham
John Spencer
Nicholas J. Kain
Rich Felker
Richard Pennington
Stefan Kristiansson
Szabolcs Nagy
all of whom have explicitly granted such permission.
This file previously contained text expressing a belief that most of
the files covered by the above exception were sufficiently trivial not
to be subject to copyright, resulting in confusion over whether it
negated the permissions granted in the license. In the spirit of
permissive licensing, and of not having licensing issues being an
obstacle to adoption, that text has been removed.

View file

@ -0,0 +1,2 @@
This set of files all come from [musl libc](https://musl.libc.org/).
Roc just directly uses a few of them instead of depending on musl libc fully.

View file

@ -0,0 +1,30 @@
.global {[function_prefix]s}musl_memcpy
// Windows does not support the type directive.
// .type {[function_prefix]s}musl_memcpy,@function
{[function_prefix]s}musl_memcpy:
push %esi
push %edi
mov 12(%esp),%edi
mov 16(%esp),%esi
mov 20(%esp),%ecx
mov %edi,%eax
cmp $4,%ecx
jc 1f
test $3,%edi
jz 1f
2: movsb
dec %ecx
test $3,%edi
jnz 2b
1: mov %ecx,%edx
shr $2,%ecx
rep
movsl
and $3,%edx
jz 1f
2: movsb
dec %edx
jnz 2b
1: pop %edi
pop %esi
ret

View file

@ -0,0 +1,23 @@
.global {[function_prefix]s}musl_memcpy
// Windows does not support the type directive.
// .type {[function_prefix]s}musl_memcpy,@function
{[function_prefix]s}musl_memcpy:
mov %rdi,%rax
cmp $8,%rdx
jc 1f
test $7,%edi
jz 1f
2: movsb
dec %rdx
test $7,%edi
jnz 2b
1: mov %rdx,%rcx
shr $3,%rcx
rep
movsq
and $7,%edx
jz 1f
2: movsb
dec %edx
jnz 2b
1: ret

View file

@ -0,0 +1,201 @@
const std = @import("std");
const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const function_prefix = @import("../assembly_util.zig").function_prefix;
comptime {
switch (arch) {
.x86_64 => {
asm (std.fmt.comptimePrint(@embedFile("memcpy-x86_64.S"), .{ .function_prefix = function_prefix }));
},
.i386 => {
asm (std.fmt.comptimePrint(@embedFile("memcpy-i386.S"), .{ .function_prefix = function_prefix }));
},
// TODO: add assembly implementations for other platforms.
else => {},
}
}
pub const memcpy =
switch (arch) {
.x86_64, .i386 => musl_memcpy,
else => fallback_memcpy,
};
pub extern fn musl_memcpy(noalias dest: [*]u8, noalias src: [*]const u8, len: usize) callconv(.C) [*]u8;
// Note: this is written to only support little endian targets.
// To support big endian, `<<` and `>>` wold need to be swapped.
pub fn fallback_memcpy(noalias dest: [*]u8, noalias src: [*]const u8, len: usize) callconv(.C) [*]u8 {
var d = dest;
var s = src;
var n = len;
while (@ptrToInt(s) % 4 != 0 and n != 0) : (n -= 1) {
d[0] = s[0];
d += 1;
s += 1;
}
if (@ptrToInt(d) % 4 == 0) {
var d4 = @alignCast(4, d);
var s4 = @alignCast(4, s);
while (n >= 16) : (n -= 16) {
var d_u32 = @ptrCast([*]u32, d4);
var s_u32 = @ptrCast([*]const u32, s4);
d_u32[0] = s_u32[0];
d_u32[1] = s_u32[1];
d_u32[2] = s_u32[2];
d_u32[3] = s_u32[3];
d4 += 16;
s4 += 16;
}
if (n & 8 != 0) {
var d_u32 = @ptrCast([*]u32, d4);
var s_u32 = @ptrCast([*]const u32, s4);
d_u32[0] = s_u32[0];
d_u32[1] = s_u32[1];
d4 += 8;
s4 += 8;
}
if (n & 4 != 0) {
var d_u32 = @ptrCast([*]u32, d4);
var s_u32 = @ptrCast([*]const u32, s4);
d_u32[0] = s_u32[0];
d4 += 4;
s4 += 4;
}
d = d4;
s = s4;
if (n & 2 != 0) {
d[0] = s[0];
d += 1;
s += 1;
d[0] = s[0];
d += 1;
s += 1;
}
if (n & 1 != 0) {
d[0] = s[0];
}
return dest;
}
if (n >= 32) {
switch (@ptrToInt(d) % 4) {
1 => {
var w = @ptrCast([*]const u32, @alignCast(4, s))[0];
d[0] = s[0];
d += 1;
s += 1;
d[0] = s[0];
d += 1;
s += 1;
d[0] = s[0];
d += 1;
s += 1;
n -= 3;
while (n >= 17) : (n -= 16) {
var d_u32 = @ptrCast([*]u32, @alignCast(4, d));
var s_u32 = @ptrCast([*]const u32, @alignCast(4, s + 1));
var x = s_u32[0];
d_u32[0] = (w >> 24) | (x << 8);
w = s_u32[1];
d_u32[1] = (x >> 24) | (w << 8);
x = s_u32[2];
d_u32[2] = (w >> 24) | (x << 8);
w = s_u32[3];
d_u32[3] = (x >> 24) | (w << 8);
d += 16;
s += 16;
}
},
2 => {
var w = @ptrCast([*]const u32, @alignCast(4, s))[0];
d[0] = s[0];
d += 1;
s += 1;
d[0] = s[0];
d += 1;
s += 1;
n -= 2;
while (n >= 18) : (n -= 16) {
var d_u32 = @ptrCast([*]u32, @alignCast(4, d));
var s_u32 = @ptrCast([*]const u32, @alignCast(4, s + 2));
var x = s_u32[0];
d_u32[0] = (w >> 16) | (x << 16);
w = s_u32[1];
d_u32[1] = (x >> 16) | (w << 16);
x = s_u32[2];
d_u32[2] = (w >> 16) | (x << 16);
w = s_u32[3];
d_u32[3] = (x >> 16) | (w << 16);
d += 16;
s += 16;
}
},
3 => {
var w = @ptrCast([*]const u32, @alignCast(4, s))[0];
d[0] = s[0];
d += 1;
s += 1;
n -= 1;
while (n >= 19) : (n -= 16) {
var d_u32 = @ptrCast([*]u32, @alignCast(4, d));
var s_u32 = @ptrCast([*]const u32, @alignCast(4, s + 3));
var x = s_u32[0];
d_u32[0] = (w >> 8) | (x << 24);
w = s_u32[1];
d_u32[1] = (x >> 8) | (w << 24);
x = s_u32[2];
d_u32[2] = (w >> 8) | (x << 24);
w = s_u32[3];
d_u32[3] = (x >> 8) | (w << 24);
d += 16;
s += 16;
}
},
else => unreachable,
}
}
if (n & 16 != 0) {
comptime var i = 0;
inline while (i < 16) : (i += 1) {
d[0] = s[0];
d += 1;
s += 1;
}
}
if (n & 8 != 0) {
comptime var i = 0;
inline while (i < 8) : (i += 1) {
d[0] = s[0];
d += 1;
s += 1;
}
}
if (n & 4 != 0) {
comptime var i = 0;
inline while (i < 4) : (i += 1) {
d[0] = s[0];
d += 1;
s += 1;
}
}
if (n & 2 != 0) {
d[0] = s[0];
d += 1;
s += 1;
d[0] = s[0];
d += 1;
s += 1;
}
if (n & 1 != 0) {
d[0] = s[0];
}
return dest;
}

View file

@ -7,6 +7,7 @@ const panic_utils = @import("panic.zig");
comptime { comptime {
_ = @import("compiler_rt.zig"); _ = @import("compiler_rt.zig");
_ = @import("libc.zig");
} }
const ROC_BUILTINS = "roc_builtins"; const ROC_BUILTINS = "roc_builtins";

View file

@ -19,9 +19,6 @@ extern fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, align
// This should never be passed a null pointer. // This should never be passed a null pointer.
extern fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void; extern fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void;
// should work just like libc memcpy (we can't assume libc is present)
extern fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
extern fn kill(pid: c_int, sig: c_int) c_int; extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
@ -49,7 +46,6 @@ comptime {
@export(testing_roc_realloc, .{ .name = "roc_realloc", .linkage = .Strong }); @export(testing_roc_realloc, .{ .name = "roc_realloc", .linkage = .Strong });
@export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong }); @export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong });
@export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong }); @export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong });
@export(testing_roc_memcpy, .{ .name = "roc_memcpy", .linkage = .Strong });
if (builtin.os.tag == .macos or builtin.os.tag == .linux) { if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(testing_roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); @export(testing_roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@ -83,14 +79,6 @@ fn testing_roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
@panic("Roc panicked"); @panic("Roc panicked");
} }
fn testing_roc_memcpy(dest: *anyopaque, src: *anyopaque, bytes: usize) callconv(.C) ?*anyopaque {
const zig_dest = @ptrCast([*]u8, dest);
const zig_src = @ptrCast([*]u8, src);
@memcpy(zig_dest, zig_src, bytes);
return dest;
}
pub fn alloc(size: usize, alignment: u32) ?[*]u8 { pub fn alloc(size: usize, alignment: u32) ?[*]u8 {
return @ptrCast(?[*]u8, roc_alloc(size, alignment)); return @ptrCast(?[*]u8, roc_alloc(size, alignment));
} }
@ -103,10 +91,6 @@ pub fn dealloc(c_ptr: [*]u8, alignment: u32) void {
return roc_dealloc(c_ptr, alignment); return roc_dealloc(c_ptr, alignment);
} }
pub fn memcpy(dst: [*]u8, src: [*]u8, size: usize) void {
roc_memcpy(dst, src, size);
}
// indirection because otherwise zig creates an alias to the panic function which our LLVM code // indirection because otherwise zig creates an alias to the panic function which our LLVM code
// does not know how to deal with // does not know how to deal with
pub fn test_panic(c_ptr: *anyopaque, crash_tag: u32) callconv(.C) void { pub fn test_panic(c_ptr: *anyopaque, crash_tag: u32) callconv(.C) void {

View file

@ -124,18 +124,19 @@ impl<'a> Env<'a> {
Ok(symbol) Ok(symbol)
} }
None => { None => {
let error = RuntimeError::LookupNotInScope( let error = RuntimeError::LookupNotInScope {
Loc { loc_name: Loc {
value: Ident::from(ident), value: Ident::from(ident),
region, region,
}, },
scope suggestion_options: scope
.locals .locals
.ident_ids .ident_ids
.ident_strs() .ident_strs()
.map(|(_, string)| string.into()) .map(|(_, string)| string.into())
.collect(), .collect(),
); underscored_suggestion_region: None,
};
Err(error) Err(error)
} }
} }

View file

@ -1019,9 +1019,18 @@ pub fn canonicalize_expr<'a>(
} }
ast::Expr::Underscore(name) => { ast::Expr::Underscore(name) => {
// we parse underscores, but they are not valid expression syntax // we parse underscores, but they are not valid expression syntax
let problem = roc_problem::can::RuntimeError::MalformedIdentifier( let problem = roc_problem::can::RuntimeError::MalformedIdentifier(
(*name).into(), (*name).into(),
roc_parse::ident::BadIdent::Underscore(region.start()), if name.is_empty() {
roc_parse::ident::BadIdent::UnderscoreAlone(region.start())
} else {
roc_parse::ident::BadIdent::UnderscoreAtStart {
position: region.start(),
// Check if there's an ignored identifier with this name in scope (for better error messages)
declaration_region: scope.lookup_ignored_local(name),
}
},
region, region,
); );

View file

@ -379,6 +379,12 @@ pub fn canonicalize_pattern<'a>(
Err(pattern) => pattern, Err(pattern) => pattern,
} }
} }
Underscore(name) => {
// An underscored identifier can't be used, but we'll still add it to the scope
// for better error messages if someone tries to use it.
scope.introduce_ignored_local(name, region);
Pattern::Underscore
}
Tag(name) => { Tag(name) => {
// Canonicalize the tag's name. // Canonicalize the tag's name.
Pattern::AppliedTag { Pattern::AppliedTag {
@ -479,8 +485,6 @@ pub fn canonicalize_pattern<'a>(
ptype => unsupported_pattern(env, ptype, region), ptype => unsupported_pattern(env, ptype, region),
}, },
Underscore(_) => Pattern::Underscore,
&NumLiteral(str) => match pattern_type { &NumLiteral(str) => match pattern_type {
WhenBranch => match finish_parsing_num(str) { WhenBranch => match finish_parsing_num(str) {
Err(_error) => { Err(_error) => {

View file

@ -41,6 +41,10 @@ pub struct Scope {
/// Identifiers that are in scope, and defined in the current module /// Identifiers that are in scope, and defined in the current module
pub locals: ScopedIdentIds, pub locals: ScopedIdentIds,
/// Ignored variables (variables that start with an underscore).
/// We won't intern them because they're only used during canonicalization for error reporting.
ignored_locals: VecMap<String, Region>,
} }
impl Scope { impl Scope {
@ -65,6 +69,7 @@ impl Scope {
abilities_store: starting_abilities_store, abilities_store: starting_abilities_store,
shadows: VecMap::default(), shadows: VecMap::default(),
imports: default_imports, imports: default_imports,
ignored_locals: VecMap::default(),
} }
} }
@ -89,13 +94,17 @@ impl Scope {
match self.scope_contains_ident(ident) { match self.scope_contains_ident(ident) {
InScope(symbol, _) => Ok(symbol), InScope(symbol, _) => Ok(symbol),
NotInScope(_) | NotPresent => { NotInScope(_) | NotPresent => {
let error = RuntimeError::LookupNotInScope( // identifier not found
Loc {
let error = RuntimeError::LookupNotInScope {
loc_name: Loc {
region, region,
value: Ident::from(ident), value: Ident::from(ident),
}, },
self.idents_in_scope().map(|v| v.as_ref().into()).collect(), suggestion_options: self.idents_in_scope().map(|v| v.as_ref().into()).collect(),
); // Check if the user just forgot to remove an underscore from an ignored identifier
underscored_suggestion_region: self.lookup_ignored_local(ident),
};
Err(error) Err(error)
} }
@ -418,11 +427,13 @@ impl Scope {
// - exposed_ident_count: unchanged // - exposed_ident_count: unchanged
// - home: unchanged // - home: unchanged
let aliases_count = self.aliases.len(); let aliases_count = self.aliases.len();
let ignored_locals_count = self.ignored_locals.len();
let locals_snapshot = self.locals.in_scope.len(); let locals_snapshot = self.locals.in_scope.len();
let result = f(self); let result = f(self);
self.aliases.truncate(aliases_count); self.aliases.truncate(aliases_count);
self.ignored_locals.truncate(ignored_locals_count);
// anything added in the inner scope is no longer in scope now // anything added in the inner scope is no longer in scope now
for i in locals_snapshot..self.locals.in_scope.len() { for i in locals_snapshot..self.locals.in_scope.len() {
@ -444,6 +455,19 @@ impl Scope {
pub fn gen_unique_symbol(&mut self) -> Symbol { pub fn gen_unique_symbol(&mut self) -> Symbol {
Symbol::new(self.home, self.locals.gen_unique()) Symbol::new(self.home, self.locals.gen_unique())
} }
/// Introduce a new ignored variable (variable starting with an underscore).
/// The underscore itself should not be included in `ident`.
pub fn introduce_ignored_local(&mut self, ident: &str, region: Region) {
self.ignored_locals.insert(ident.to_owned(), region);
}
/// Lookup an ignored variable (variable starting with an underscore).
/// The underscore itself should not be included in `ident`.
/// Returns the source code region of the ignored variable if it's found.
pub fn lookup_ignored_local(&self, ident: &str) -> Option<Region> {
self.ignored_locals.get(&ident.to_owned()).copied()
}
} }
pub fn create_alias( pub fn create_alias(

View file

@ -332,7 +332,7 @@ mod test_can {
matches!( matches!(
problem, problem,
Problem::SignatureDefMismatch { .. } Problem::SignatureDefMismatch { .. }
| Problem::RuntimeError(RuntimeError::LookupNotInScope(_, _)) | Problem::RuntimeError(RuntimeError::LookupNotInScope { .. })
) )
})); }));
} }
@ -360,7 +360,7 @@ mod test_can {
matches!( matches!(
problem, problem,
Problem::SignatureDefMismatch { .. } Problem::SignatureDefMismatch { .. }
| Problem::RuntimeError(RuntimeError::LookupNotInScope(_, _)) | Problem::RuntimeError(RuntimeError::LookupNotInScope { .. })
) )
})); }));
} }

View file

@ -761,7 +761,15 @@ fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
match ident { match ident {
BadIdent::Start(_) => BadIdent::Start(Position::zero()), BadIdent::Start(_) => BadIdent::Start(Position::zero()),
BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()), BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()),
BadIdent::Underscore(_) => BadIdent::Underscore(Position::zero()), BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()),
BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()),
BadIdent::UnderscoreAtStart {
position: _,
declaration_region,
} => BadIdent::UnderscoreAtStart {
position: Position::zero(),
declaration_region,
},
BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()), BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()),
BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()), BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()),
BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()), BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()),

View file

@ -2859,7 +2859,15 @@ pub fn build_exp_stmt<'a, 'ctx>(
_ if lay.is_refcounted() => { _ if lay.is_refcounted() => {
if value.is_pointer_value() { if value.is_pointer_value() {
let value_ptr = value.into_pointer_value(); let value_ptr = match lay.repr {
LayoutRepr::Union(union_layout)
if union_layout
.stores_tag_id_in_pointer(env.target_info) =>
{
tag_pointer_clear_tag_id(env, value.into_pointer_value())
}
_ => value.into_pointer_value(),
};
let then_block = env.context.append_basic_block(parent, "then"); let then_block = env.context.append_basic_block(parent, "then");
let done_block = env.context.append_basic_block(parent, "done"); let done_block = env.context.append_basic_block(parent, "done");

View file

@ -289,11 +289,6 @@ mod dummy_platform_functions {
unimplemented!("It is not valid to call roc panic from within the compiler. Please use the \"platform\" feature if this is a platform.") unimplemented!("It is not valid to call roc panic from within the compiler. Please use the \"platform\" feature if this is a platform.")
} }
#[no_mangle]
pub fn roc_memcpy(_dst: *mut c_void, _src: *mut c_void, _n: usize) -> *mut c_void {
unimplemented!("It is not valid to call roc memcpy from within the compiler. Please use the \"platform\" feature if this is a platform.")
}
#[no_mangle] #[no_mangle]
pub fn roc_memset(_dst: *mut c_void, _c: i32, _n: usize) -> *mut c_void { pub fn roc_memset(_dst: *mut c_void, _c: i32, _n: usize) -> *mut c_void {
unimplemented!("It is not valid to call roc memset from within the compiler. Please use the \"platform\" feature if this is a platform.") unimplemented!("It is not valid to call roc memset from within the compiler. Please use the \"platform\" feature if this is a platform.")

View file

@ -53,6 +53,8 @@ use roc_parse::module::module_defs;
use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError}; use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError};
use roc_problem::Severity; use roc_problem::Severity;
use roc_region::all::{LineInfo, Loc, Region}; use roc_region::all::{LineInfo, Loc, Region};
#[cfg(not(target_family = "wasm"))]
use roc_reporting::report::to_https_problem_report_string;
use roc_reporting::report::{to_file_problem_report_string, Palette, RenderTarget}; use roc_reporting::report::{to_file_problem_report_string, Palette, RenderTarget};
use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule}; use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule};
use roc_solve_problem::TypeError; use roc_solve_problem::TypeError;
@ -72,7 +74,7 @@ use std::{env, fs};
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
use { use {
roc_packaging::cache::{self}, roc_packaging::cache::{self},
roc_packaging::https::PackageMetadata, roc_packaging::https::{PackageMetadata, Problem},
}; };
pub use crate::work::Phase; pub use crate::work::Phase;
@ -2454,11 +2456,11 @@ fn update<'a>(
} }
} }
Err(url_err) => { Err(url_err) => {
todo!( let buf = to_https_problem_report_string(
"Gracefully report URL error for {:?} - {:?}",
url, url,
url_err Problem::InvalidUrl(url_err),
); );
return Err(LoadingProblem::FormattedReport(buf));
} }
} }
} }
@ -4329,11 +4331,8 @@ fn load_packages<'a>(
// TODO we should do this async; however, with the current // TODO we should do this async; however, with the current
// architecture of file.rs (which doesn't use async/await), // architecture of file.rs (which doesn't use async/await),
// this would be very difficult! // this would be very difficult!
let (package_dir, opt_root_module) = cache::install_package(roc_cache_dir, src) match cache::install_package(roc_cache_dir, src) {
.unwrap_or_else(|err| { Ok((package_dir, opt_root_module)) => {
todo!("TODO gracefully handle package install error {:?}", err);
});
// You can optionally specify the root module using the URL fragment, // You can optionally specify the root module using the URL fragment,
// e.g. #foo.roc // e.g. #foo.roc
// (defaults to main.roc) // (defaults to main.roc)
@ -4342,6 +4341,14 @@ fn load_packages<'a>(
None => package_dir.join("main.roc"), None => package_dir.join("main.roc"),
} }
} }
Err(problem) => {
let buf = to_https_problem_report_string(src, problem);
load_messages.push(Msg::FailedToLoad(LoadingProblem::FormattedReport(buf)));
return;
}
}
}
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
{ {

View file

@ -3,7 +3,7 @@ use crate::parser::{BadInputError, EExpr, ParseResult, Parser};
use crate::state::State; use crate::state::State;
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_region::all::Position; use roc_region::all::{Position, Region};
/// A tag, for example. Must start with an uppercase letter /// A tag, for example. Must start with an uppercase letter
/// and then contain only letters and numbers afterwards - no dots allowed! /// and then contain only letters and numbers afterwards - no dots allowed!
@ -254,7 +254,14 @@ pub enum BadIdent {
Start(Position), Start(Position),
Space(BadInputError, Position), Space(BadInputError, Position),
Underscore(Position), UnderscoreAlone(Position),
UnderscoreInMiddle(Position),
UnderscoreAtStart {
position: Position,
/// If this variable was already declared in a pattern (e.g. \_x -> _x),
/// then this is where it was declared.
declaration_region: Option<Region>,
},
QualifiedTag(Position), QualifiedTag(Position),
WeirdAccessor(Position), WeirdAccessor(Position),
WeirdDotAccess(Position), WeirdDotAccess(Position),
@ -529,7 +536,7 @@ fn chomp_identifier_chain<'a>(
// to give good error messages for this case // to give good error messages for this case
Err(( Err((
chomped as u32 + 1, chomped as u32 + 1,
BadIdent::Underscore(pos.bump_column(chomped as u32 + 1)), BadIdent::UnderscoreInMiddle(pos.bump_column(chomped as u32 + 1)),
)) ))
} else if first_is_uppercase { } else if first_is_uppercase {
// just one segment, starting with an uppercase letter; that's a tag // just one segment, starting with an uppercase letter; that's a tag

View file

@ -335,7 +335,11 @@ impl Problem {
}) })
| Problem::RuntimeError(RuntimeError::UnsupportedPattern(region)) | Problem::RuntimeError(RuntimeError::UnsupportedPattern(region))
| Problem::RuntimeError(RuntimeError::MalformedPattern(_, region)) | Problem::RuntimeError(RuntimeError::MalformedPattern(_, region))
| Problem::RuntimeError(RuntimeError::LookupNotInScope(Loc { region, .. }, _)) | Problem::RuntimeError(RuntimeError::LookupNotInScope {
loc_name: Loc { region, .. },
suggestion_options: _,
underscored_suggestion_region: _,
})
| Problem::RuntimeError(RuntimeError::OpaqueNotDefined { | Problem::RuntimeError(RuntimeError::OpaqueNotDefined {
usage: Loc { region, .. }, usage: Loc { region, .. },
.. ..
@ -505,7 +509,14 @@ pub enum RuntimeError {
UnresolvedTypeVar, UnresolvedTypeVar,
ErroneousType, ErroneousType,
LookupNotInScope(Loc<Ident>, MutSet<Box<str>>), LookupNotInScope {
loc_name: Loc<Ident>,
/// All of the names in scope (for the error message)
suggestion_options: MutSet<Box<str>>,
/// If the unfound variable is `name` and there's an ignored variable called `_name`,
/// this is the region where `_name` is defined (for the error message)
underscored_suggestion_region: Option<Region>,
},
OpaqueNotDefined { OpaqueNotDefined {
usage: Loc<Ident>, usage: Loc<Ident>,
opaques_in_scope: MutSet<Box<str>>, opaques_in_scope: MutSet<Box<str>>,

View file

@ -14,6 +14,8 @@ use roc_mono::layout::{LayoutRepr, STLayoutInterner};
#[cfg(test)] #[cfg(test)]
use roc_std::{RocList, RocStr, U128}; use roc_std::{RocList, RocStr, U128};
use crate::helpers::with_larger_debug_stack;
#[test] #[test]
fn width_and_alignment_u8_u8() { fn width_and_alignment_u8_u8() {
use roc_mono::layout::Layout; use roc_mono::layout::Layout;
@ -2167,6 +2169,7 @@ fn refcount_nullable_unwrapped_needing_no_refcount_issue_5027() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn issue_5162_recast_nested_nullable_unwrapped_layout() { fn issue_5162_recast_nested_nullable_unwrapped_layout() {
with_larger_debug_stack(|| {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r###" r###"
@ -2188,6 +2191,7 @@ fn issue_5162_recast_nested_nullable_unwrapped_layout() {
true, true,
bool bool
); );
});
} }
#[test] #[test]

View file

@ -7,13 +7,6 @@ pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
libc::malloc(size) libc::malloc(size)
} }
/// # Safety
/// The Roc application needs this.
#[no_mangle]
pub unsafe fn roc_memcpy(dest: *mut c_void, src: *const c_void, bytes: usize) -> *mut c_void {
libc::memcpy(dest, src, bytes)
}
/// # Safety /// # Safety
/// The Roc application needs this. /// The Roc application needs this.
#[no_mangle] #[no_mangle]

View file

@ -134,13 +134,6 @@ void roc_panic(void* msg, unsigned int tag_id)
//-------------------------- //--------------------------
void roc_memcpy(void *dest, const void *src, size_t n)
{
memcpy(dest, src, n);
}
//--------------------------
void *roc_memset(void *str, int c, size_t n) void *roc_memset(void *str, int c, size_t n)
{ {
return memset(str, c, n); return memset(str, c, n);

View file

@ -2,7 +2,7 @@ Closure(
[ [
@1-11 MalformedIdent( @1-11 MalformedIdent(
"the_answer", "the_answer",
Underscore( UnderscoreInMiddle(
@5, @5,
), ),
), ),

View file

@ -31,7 +31,7 @@ Defs(
@5-8 SpaceBefore( @5-8 SpaceBefore(
MalformedIdent( MalformedIdent(
"n_p", "n_p",
Underscore( UnderscoreInMiddle(
@7, @7,
), ),
), ),

View file

@ -78,11 +78,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -49,11 +49,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -71,11 +71,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -89,11 +89,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -75,11 +75,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -88,11 +88,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -76,11 +76,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -73,11 +73,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -91,11 +91,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -87,11 +87,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -102,11 +102,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -51,11 +51,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -51,11 +51,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -51,11 +51,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -77,11 +77,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -82,11 +82,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -79,11 +79,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -140,7 +140,6 @@ fn collect_roc_definitions<'a>(object: &object::File<'a, &'a [u8]>) -> MutMap<St
// special exceptions for roc_ functions that map to libc symbols // special exceptions for roc_ functions that map to libc symbols
let direct_mapping = match name { let direct_mapping = match name {
"roc_memcpy" => Some("memcpy"),
"roc_memset" => Some("memset"), "roc_memset" => Some("memset"),
"roc_memmove" => Some("memmove"), "roc_memmove" => Some("memmove"),
@ -603,7 +602,7 @@ fn gen_elf_le(
} }
// Copy header and shift everything to enable more program sections. // Copy header and shift everything to enable more program sections.
let added_header_count = 2; let added_header_count = 3;
md.added_byte_count = ph_ent_size as u64 * added_header_count; md.added_byte_count = ph_ent_size as u64 * added_header_count;
md.added_byte_count = md.added_byte_count md.added_byte_count = md.added_byte_count
+ (MIN_SECTION_ALIGNMENT as u64 - md.added_byte_count % MIN_SECTION_ALIGNMENT as u64); + (MIN_SECTION_ALIGNMENT as u64 - md.added_byte_count % MIN_SECTION_ALIGNMENT as u64);
@ -1278,22 +1277,12 @@ fn surgery_elf_help(
let mut offset = sh_offset as usize; let mut offset = sh_offset as usize;
offset = align_by_constraint(offset, MIN_SECTION_ALIGNMENT); offset = align_by_constraint(offset, MIN_SECTION_ALIGNMENT);
let new_rodata_section_offset = offset;
// Align physical and virtual address of new segment. // Align physical and virtual address of new segment.
let mut virt_offset = align_to_offset_by_constraint( let mut virt_offset = align_to_offset_by_constraint(
md.last_vaddr as usize, md.last_vaddr as usize,
offset, offset,
md.load_align_constraint as usize, md.load_align_constraint as usize,
); );
let new_rodata_section_vaddr = virt_offset;
if verbose {
println!();
println!(
"New Virtual Rodata Section Address: {:+x?}",
new_rodata_section_vaddr
);
}
// First decide on sections locations and then recode every exact symbol locations. // First decide on sections locations and then recode every exact symbol locations.
@ -1375,13 +1364,40 @@ fn surgery_elf_help(
println!("Found App Function Symbols: {:+x?}", app_func_vaddr_map); println!("Found App Function Symbols: {:+x?}", app_func_vaddr_map);
} }
let (new_rodata_section_offset, new_rodata_section_vaddr) = rodata_sections
.iter()
.map(|sec| section_offset_map.get(&sec.index()).unwrap())
.min()
.unwrap();
let (new_rodata_section_offset, new_rodata_section_vaddr) = (
*new_rodata_section_offset as u64,
*new_rodata_section_vaddr as u64,
);
let (new_text_section_offset, new_text_section_vaddr) = text_sections let (new_text_section_offset, new_text_section_vaddr) = text_sections
.iter() .iter()
.map(|sec| section_offset_map.get(&sec.index()).unwrap()) .map(|sec| section_offset_map.get(&sec.index()).unwrap())
.min() .min()
.unwrap(); .unwrap();
let (new_text_section_offset, new_text_section_vaddr) = let (new_text_section_offset, new_text_section_vaddr) = (
(*new_text_section_offset, *new_text_section_vaddr); *new_text_section_offset as u64,
*new_text_section_vaddr as u64,
);
// BSS section is not guaranteed to exist.
// If it doesn't exist, just use the text section offset.
// This will make a bss section of size 0.
let bss_default = (
new_text_section_offset as usize,
new_text_section_vaddr as usize,
);
let (new_bss_section_offset, new_bss_section_vaddr) = bss_sections
.iter()
.map(|sec| section_offset_map.get(&sec.index()).unwrap())
.min()
.unwrap_or(&bss_default);
let (new_bss_section_offset, new_bss_section_vaddr) = (
*new_bss_section_offset as u64,
*new_bss_section_vaddr as u64,
);
// Move data and deal with relocations. // Move data and deal with relocations.
for sec in rodata_sections for sec in rodata_sections
@ -1494,15 +1510,15 @@ fn surgery_elf_help(
// Flush app only data to speed up write to disk. // Flush app only data to speed up write to disk.
exec_mmap exec_mmap
.flush_async_range( .flush_async_range(
new_rodata_section_offset, new_rodata_section_offset as usize,
offset - new_rodata_section_offset, offset - new_rodata_section_offset as usize,
) )
.unwrap_or_else(|e| internal_error!("{}", e)); .unwrap_or_else(|e| internal_error!("{}", e));
// TODO: look into merging symbol tables, debug info, and eh frames to enable better debugger experience. // TODO: look into merging symbol tables, debug info, and eh frames to enable better debugger experience.
// Add 2 new sections and segments. // Add 3 new sections and segments.
let new_section_count = 2; let new_section_count = 3;
offset += new_section_count * sh_ent_size as usize; offset += new_section_count * sh_ent_size as usize;
let section_headers = load_structs_inplace_mut::<elf::SectionHeader64<LE>>( let section_headers = load_structs_inplace_mut::<elf::SectionHeader64<LE>>(
exec_mmap, exec_mmap,
@ -1510,19 +1526,17 @@ fn surgery_elf_help(
sh_num as usize + new_section_count, sh_num as usize + new_section_count,
); );
let new_rodata_section_size = new_text_section_offset as u64 - new_rodata_section_offset as u64; let new_rodata_section_size = new_text_section_offset - new_rodata_section_offset;
let new_rodata_section_virtual_size = let new_bss_section_virtual_size = new_text_section_vaddr - new_bss_section_vaddr;
new_text_section_vaddr as u64 - new_rodata_section_vaddr as u64; let new_text_section_size = new_sh_offset as u64 - new_text_section_offset;
let new_text_section_vaddr = new_rodata_section_vaddr as u64 + new_rodata_section_size;
let new_text_section_size = new_sh_offset as u64 - new_text_section_offset as u64;
// set the new rodata section header // set the new rodata section header
section_headers[section_headers.len() - 2] = elf::SectionHeader64 { section_headers[section_headers.len() - 3] = elf::SectionHeader64 {
sh_name: endian::U32::new(LE, 0), sh_name: endian::U32::new(LE, 0),
sh_type: endian::U32::new(LE, elf::SHT_PROGBITS), sh_type: endian::U32::new(LE, elf::SHT_PROGBITS),
sh_flags: endian::U64::new(LE, elf::SHF_ALLOC as u64), sh_flags: endian::U64::new(LE, elf::SHF_ALLOC as u64),
sh_addr: endian::U64::new(LE, new_rodata_section_vaddr as u64), sh_addr: endian::U64::new(LE, new_rodata_section_vaddr),
sh_offset: endian::U64::new(LE, new_rodata_section_offset as u64), sh_offset: endian::U64::new(LE, new_rodata_section_offset),
sh_size: endian::U64::new(LE, new_rodata_section_size), sh_size: endian::U64::new(LE, new_rodata_section_size),
sh_link: endian::U32::new(LE, 0), sh_link: endian::U32::new(LE, 0),
sh_info: endian::U32::new(LE, 0), sh_info: endian::U32::new(LE, 0),
@ -1530,13 +1544,27 @@ fn surgery_elf_help(
sh_entsize: endian::U64::new(LE, 0), sh_entsize: endian::U64::new(LE, 0),
}; };
// set the new bss section header
section_headers[section_headers.len() - 2] = elf::SectionHeader64 {
sh_name: endian::U32::new(LE, 0),
sh_type: endian::U32::new(LE, elf::SHT_NOBITS),
sh_flags: endian::U64::new(LE, (elf::SHF_ALLOC) as u64),
sh_addr: endian::U64::new(LE, new_bss_section_vaddr),
sh_offset: endian::U64::new(LE, new_bss_section_offset),
sh_size: endian::U64::new(LE, new_bss_section_virtual_size),
sh_link: endian::U32::new(LE, 0),
sh_info: endian::U32::new(LE, 0),
sh_addralign: endian::U64::new(LE, 16),
sh_entsize: endian::U64::new(LE, 0),
};
// set the new text section header // set the new text section header
section_headers[section_headers.len() - 1] = elf::SectionHeader64 { section_headers[section_headers.len() - 1] = elf::SectionHeader64 {
sh_name: endian::U32::new(LE, 0), sh_name: endian::U32::new(LE, 0),
sh_type: endian::U32::new(LE, elf::SHT_PROGBITS), sh_type: endian::U32::new(LE, elf::SHT_PROGBITS),
sh_flags: endian::U64::new(LE, (elf::SHF_ALLOC | elf::SHF_EXECINSTR) as u64), sh_flags: endian::U64::new(LE, (elf::SHF_ALLOC | elf::SHF_EXECINSTR) as u64),
sh_addr: endian::U64::new(LE, new_text_section_vaddr), sh_addr: endian::U64::new(LE, new_text_section_vaddr),
sh_offset: endian::U64::new(LE, new_text_section_offset as u64), sh_offset: endian::U64::new(LE, new_text_section_offset),
sh_size: endian::U64::new(LE, new_text_section_size), sh_size: endian::U64::new(LE, new_text_section_size),
sh_link: endian::U32::new(LE, 0), sh_link: endian::U32::new(LE, 0),
sh_info: endian::U32::new(LE, 0), sh_info: endian::U32::new(LE, 0),
@ -1559,14 +1587,26 @@ fn surgery_elf_help(
); );
// set the new rodata section program header // set the new rodata section program header
program_headers[program_headers.len() - 2] = elf::ProgramHeader64 { program_headers[program_headers.len() - 3] = elf::ProgramHeader64 {
p_type: endian::U32::new(LE, elf::PT_LOAD), p_type: endian::U32::new(LE, elf::PT_LOAD),
p_flags: endian::U32::new(LE, elf::PF_R), p_flags: endian::U32::new(LE, elf::PF_R),
p_offset: endian::U64::new(LE, new_rodata_section_offset as u64), p_offset: endian::U64::new(LE, new_rodata_section_offset),
p_vaddr: endian::U64::new(LE, new_rodata_section_vaddr as u64), p_vaddr: endian::U64::new(LE, new_rodata_section_vaddr),
p_paddr: endian::U64::new(LE, new_rodata_section_vaddr as u64), p_paddr: endian::U64::new(LE, new_rodata_section_vaddr),
p_filesz: endian::U64::new(LE, new_rodata_section_size), p_filesz: endian::U64::new(LE, new_rodata_section_size),
p_memsz: endian::U64::new(LE, new_rodata_section_virtual_size), p_memsz: endian::U64::new(LE, new_rodata_section_size),
p_align: endian::U64::new(LE, md.load_align_constraint),
};
// set the new bss section program header
program_headers[program_headers.len() - 2] = elf::ProgramHeader64 {
p_type: endian::U32::new(LE, elf::PT_LOAD),
p_flags: endian::U32::new(LE, elf::PF_R | elf::PF_W),
p_offset: endian::U64::new(LE, new_bss_section_offset),
p_vaddr: endian::U64::new(LE, new_bss_section_vaddr),
p_paddr: endian::U64::new(LE, new_bss_section_vaddr),
p_filesz: endian::U64::new(LE, 0),
p_memsz: endian::U64::new(LE, new_bss_section_virtual_size),
p_align: endian::U64::new(LE, md.load_align_constraint), p_align: endian::U64::new(LE, md.load_align_constraint),
}; };
@ -1575,7 +1615,7 @@ fn surgery_elf_help(
program_headers[new_text_section_index] = elf::ProgramHeader64 { program_headers[new_text_section_index] = elf::ProgramHeader64 {
p_type: endian::U32::new(LE, elf::PT_LOAD), p_type: endian::U32::new(LE, elf::PT_LOAD),
p_flags: endian::U32::new(LE, elf::PF_R | elf::PF_X), p_flags: endian::U32::new(LE, elf::PF_R | elf::PF_X),
p_offset: endian::U64::new(LE, new_text_section_offset as u64), p_offset: endian::U64::new(LE, new_text_section_offset),
p_vaddr: endian::U64::new(LE, new_text_section_vaddr), p_vaddr: endian::U64::new(LE, new_text_section_vaddr),
p_paddr: endian::U64::new(LE, new_text_section_vaddr), p_paddr: endian::U64::new(LE, new_text_section_vaddr),
p_filesz: endian::U64::new(LE, new_text_section_size), p_filesz: endian::U64::new(LE, new_text_section_size),
@ -1722,7 +1762,6 @@ mod tests {
assert_eq!( assert_eq!(
[ [
"memcpy",
"memset", "memset",
"roc_alloc", "roc_alloc",
"roc_dealloc", "roc_dealloc",

View file

@ -141,9 +141,7 @@ fn collect_roc_definitions<'a>(object: &object::File<'a, &'a [u8]>) -> MutMap<St
let address = sym.address(); let address = sym.address();
// special exceptions for memcpy and memset. // special exceptions for memcpy and memset.
if name == "roc_memcpy" { if name == "roc_memset" {
vaddresses.insert("memcpy".to_string(), address);
} else if name == "roc_memset" {
vaddresses.insert("memset".to_string(), address); vaddresses.insert("memset".to_string(), address);
} }

View file

@ -444,6 +444,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b
"__fixsfti", "__fixsfti",
"__fixunsdfti", "__fixunsdfti",
"__fixunssfti", "__fixunssfti",
"memcpy_decision",
] ]
.contains(&name.as_str()); .contains(&name.as_str());
if *address == 0 && !name.starts_with("roc") && !is_ingested_compiler_rt { if *address == 0 && !name.starts_with("roc") && !is_ingested_compiler_rt {
@ -1318,7 +1319,6 @@ fn relocate_dummy_dll_entries(executable: &mut [u8], md: &PeMetadata) {
/// Redirect `memcpy` and similar libc functions to their roc equivalents /// Redirect `memcpy` and similar libc functions to their roc equivalents
pub(crate) fn redirect_libc_functions(name: &str) -> Option<&str> { pub(crate) fn redirect_libc_functions(name: &str) -> Option<&str> {
match name { match name {
"memcpy" => Some("roc_memcpy"),
"memset" => Some("roc_memset"), "memset" => Some("roc_memset"),
"memmove" => Some("roc_memmove"), "memmove" => Some("roc_memmove"),
_ => None, _ => None,

View file

@ -64,13 +64,6 @@ void roc_panic(void *ptr, unsigned int alignment)
//-------------------------- //--------------------------
void *roc_memcpy(void *dest, const void *src, size_t n)
{
return memcpy(dest, src, n);
}
//--------------------------
void *roc_memset(void *str, int c, size_t n) void *roc_memset(void *str, int c, size_t n)
{ {
return memset(str, c, n); return memset(str, c, n);

View file

@ -15,12 +15,14 @@ roc_exhaustive = { path = "../compiler/exhaustive" }
roc_fmt = { path = "../compiler/fmt" } roc_fmt = { path = "../compiler/fmt" }
roc_module = { path = "../compiler/module" } roc_module = { path = "../compiler/module" }
roc_parse = { path = "../compiler/parse" } roc_parse = { path = "../compiler/parse" }
roc_packaging = { path = "../packaging" }
roc_problem = { path = "../compiler/problem" } roc_problem = { path = "../compiler/problem" }
roc_region = { path = "../compiler/region" } roc_region = { path = "../compiler/region" }
roc_solve_problem = { path = "../compiler/solve_problem" } roc_solve_problem = { path = "../compiler/solve_problem" }
roc_std = { path = "../roc_std" } roc_std = { path = "../roc_std" }
roc_types = { path = "../compiler/types" } roc_types = { path = "../compiler/types" }
ven_pretty = { path = "../vendor/pretty" } ven_pretty = { path = "../vendor/pretty" }
byte-unit = "4.0.19"
itertools = "0.10.5" itertools = "0.10.5"
bumpalo.workspace = true bumpalo.workspace = true

View file

@ -1254,19 +1254,49 @@ fn to_bad_ident_expr_report<'b>(
]) ])
} }
Underscore(pos) => { UnderscoreAlone(_pos) => {
let region = Region::new(surroundings.start(), pos); alloc.stack([
alloc.reflow("An underscore is being used as a variable here:"),
alloc.region(lines.convert_region(surroundings)),
alloc.concat([alloc
.reflow(r"An underscore can be used to ignore a value when pattern matching, but it cannot be used as a variable.")]),
])
}
UnderscoreInMiddle(_pos) => {
alloc.stack([ alloc.stack([
alloc.reflow("Underscores are not allowed in identifier names:"), alloc.reflow("Underscores are not allowed in identifier names:"),
alloc.region_with_subregion( alloc.region(lines.convert_region(surroundings)),
lines.convert_region(surroundings),
lines.convert_region(region),
),
alloc.concat([alloc alloc.concat([alloc
.reflow(r"I recommend using camelCase. It's the standard style in Roc code!")]), .reflow(r"I recommend using camelCase. It's the standard style in Roc code!")]),
]) ])
} }
UnderscoreAtStart {
position: _pos,
declaration_region,
} => {
let line = "This variable's name starts with an underscore:";
alloc.stack([
match declaration_region {
None => alloc.reflow(line),
Some(declaration_region) => alloc.stack([
alloc.reflow(line),
alloc.region(lines.convert_region(declaration_region)),
alloc.reflow("But then it is used here:"),
])
},
alloc.region(lines.convert_region(surroundings)),
alloc.concat([
alloc.reflow(r"A variable's name can only start with an underscore if the variable is unused. "),
match declaration_region {
None => alloc.reflow(r"But it looks like the variable is being used here!"),
Some(_) => alloc.reflow(r"Since you are using this variable, you could remove the underscore from its name in both places."),
}
]),
])
}
BadOpaqueRef(pos) => { BadOpaqueRef(pos) => {
use BadIdentNext::*; use BadIdentNext::*;
let kind = "an opaque reference"; let kind = "an opaque reference";
@ -1409,7 +1439,13 @@ fn to_bad_ident_pattern_report<'b>(
]) ])
} }
Underscore(pos) => { UnderscoreAlone(..) | UnderscoreAtStart { .. } => {
unreachable!(
"it's fine to have an underscore at the beginning of an identifier in a pattern"
)
}
UnderscoreInMiddle(pos) => {
let region = Region::from_pos(pos.sub(1)); let region = Region::from_pos(pos.sub(1));
alloc.stack([ alloc.stack([
@ -1580,8 +1616,19 @@ fn pretty_runtime_error<'b>(
(title, doc) = report_shadowing(alloc, lines, original_region, shadow, kind); (title, doc) = report_shadowing(alloc, lines, original_region, shadow, kind);
} }
RuntimeError::LookupNotInScope(loc_name, options) => { RuntimeError::LookupNotInScope {
doc = not_found(alloc, lines, loc_name.region, &loc_name.value, options); loc_name,
suggestion_options: options,
underscored_suggestion_region,
} => {
doc = not_found(
alloc,
lines,
loc_name.region,
&loc_name.value,
options,
underscored_suggestion_region,
);
title = UNRECOGNIZED_NAME; title = UNRECOGNIZED_NAME;
} }
RuntimeError::CircularDef(entries) => { RuntimeError::CircularDef(entries) => {
@ -2219,6 +2266,7 @@ fn not_found<'b>(
region: roc_region::all::Region, region: roc_region::all::Region,
name: &Ident, name: &Ident,
options: MutSet<Box<str>>, options: MutSet<Box<str>>,
underscored_suggestion_region: Option<Region>,
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
let mut suggestions = suggest::sort( let mut suggestions = suggest::sort(
name.as_inline_str().as_str(), name.as_inline_str().as_str(),
@ -2234,7 +2282,15 @@ fn not_found<'b>(
alloc.reflow(" missing up-top"), alloc.reflow(" missing up-top"),
]); ]);
let default_yes = alloc.reflow("Did you mean one of these?"); let default_yes = match underscored_suggestion_region {
Some(underscored_region) => alloc.stack([
alloc.reflow("There is an ignored identifier of a similar name here:"),
alloc.region(lines.convert_region(underscored_region)),
alloc.reflow("Did you mean to remove the leading underscore?"),
alloc.reflow("If not, did you mean one of these?"),
]),
None => alloc.reflow("Did you mean one of these?"),
};
let to_details = |no_suggestion_details, yes_suggestion_details| { let to_details = |no_suggestion_details, yes_suggestion_details| {
if suggestions.is_empty() { if suggestions.is_empty() {

View file

@ -7,6 +7,11 @@ use std::path::{Path, PathBuf};
use std::{fmt, io}; use std::{fmt, io};
use ven_pretty::{text, BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated}; use ven_pretty::{text, BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
#[cfg(not(target_family = "wasm"))]
use byte_unit::Byte;
#[cfg(not(target_family = "wasm"))]
use roc_packaging::https::Problem;
pub use crate::error::canonicalize::can_problem; pub use crate::error::canonicalize::can_problem;
pub use crate::error::parse::parse_problem; pub use crate::error::parse::parse_problem;
pub use crate::error::r#type::type_problem; pub use crate::error::r#type::type_problem;
@ -1075,6 +1080,471 @@ where
} }
} }
#[cfg(not(target_family = "wasm"))]
pub fn to_https_problem_report_string(url: &str, https_problem: Problem) -> String {
let src_lines: Vec<&str> = Vec::new();
let mut module_ids = ModuleIds::default();
let module_id = module_ids.get_or_insert(&"find module name somehow?".into());
let interns = Interns::default();
// Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
let mut buf = String::new();
let palette = DEFAULT_PALETTE;
let report = to_https_problem_report(&alloc, url, https_problem);
report.render_color_terminal(&mut buf, &alloc, &palette);
buf
}
#[cfg(not(target_family = "wasm"))]
pub fn to_https_problem_report<'b>(
alloc: &'b RocDocAllocator<'b>,
url: &'b str,
https_problem: Problem,
) -> Report<'b> {
match https_problem {
Problem::UnsupportedEncoding(not_supported_encoding) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc.string((&url).to_string()).annotate(Annotation::Url).indent(4),
alloc.concat([
alloc.reflow(r"But the server replied with a "),
alloc.reflow(r"content encoding").annotate(Annotation::Emphasized),
alloc.reflow(r" that I do not understand ("),
alloc.string(not_supported_encoding).annotate(Annotation::Emphasized),
alloc.reflow(r")."),
]),
alloc.concat([
alloc.reflow(r"The supported content encodings are "),
alloc.keyword(r"br"),
alloc.reflow(r", "),
alloc.keyword(r"gzip"),
alloc.reflow(r" and "),
alloc.keyword(r"deflate"),
]),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Perhaps you can check if the URL is correctly formed, or if the server is correctly configured."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "UNSUPPORTED ENCODING".to_string(),
severity: Severity::Fatal,
}
}
Problem::MultipleEncodings(multiple_encodings) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc.string((&url).to_string()).annotate(Annotation::Url).indent(4),
alloc.concat([
alloc.reflow(r"But the server replied with multiple "),
alloc.reflow(r"content encodings").annotate(Annotation::Emphasized),
alloc.reflow(r": "),
alloc.string(multiple_encodings).annotate(Annotation::Emphasized),
alloc.reflow(r"."),
]),
alloc.concat([
alloc.reflow(r"The supported content encodings are "),
alloc.keyword(r"br"),
alloc.reflow(r", "),
alloc.keyword(r"gzip"),
alloc.reflow(r" and "),
alloc.keyword(r"deflate"),
alloc.reflow(r". However, the server reply can only contain "),
alloc.reflow(r"one").annotate(Annotation::Emphasized),
alloc.reflow(r"."),
]),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Perhaps you can check if the URL is correctly formed, or if the server is correctly configured."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "MULTIPLE ENCODINGS".to_string(),
severity: Severity::Fatal,
}
}
Problem::InvalidContentHash { expected, actual } => {
let doc = alloc.stack([
alloc.reflow(r"I was able to download this URL:"),
alloc.string((&url).to_string()).annotate(Annotation::Url).indent(4),
alloc.concat([
alloc.reflow(r"I use a mechanism to detect if the file might "),
alloc.reflow(r"have been tampered with. This could happen if "),
alloc.reflow(r"the server or domain have been compromised."),
]),
alloc.concat([
alloc.reflow(r"This is the content signature I was "),
alloc.reflow(r"expecting").annotate(Annotation::Emphasized),
alloc.reflow(r":"),
]),
alloc.string(expected).annotate(Annotation::PlainText).indent(4),
alloc.concat([
alloc.reflow(r"However, this is the content signature I "),
alloc.reflow(r"obtained").annotate(Annotation::Emphasized),
alloc.reflow(r":"),
]),
alloc.string(actual).annotate(Annotation::PlainText).indent(4),
alloc.reflow(r"To keep you secure, I will not execute this untrusted code."),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Check if the URL is correctly formed and if this is the server you are expecting to connect to."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "INVALID CONTENT HASH".to_string(),
severity: Severity::Fatal,
}
}
// TODO: The reporting text for IoErr and FsExtraErr could probably be unified
Problem::IoErr(io_error) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.reflow(r"But I encountered an IO (input/output) error:"),
alloc
.string(io_error.to_string())
.annotate(Annotation::PlainText)
.indent(4),
// TODO: What should the tip for IO errors be?
// alloc.concat([
// alloc.tip(),
// alloc.reflow(r"Check the error message."),
// ]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "IO ERROR".to_string(),
severity: Severity::Fatal,
}
}
// TODO: The reporting text for IoErr and FsExtraErr could probably be unified
Problem::FsExtraErr(fs_extra_error) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.reflow(r"But I encountered an IO (input/output) error:"),
alloc
.string(fs_extra_error.to_string())
.annotate(Annotation::PlainText)
.indent(4),
// TODO: What should the tip for IO errors be?
// alloc.concat([
// alloc.tip(),
// alloc.reflow(r"Check the error message."),
// ]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "IO ERROR".to_string(),
severity: Severity::Fatal,
}
}
Problem::HttpErr(reqwest_error) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.reflow(r"But I encountered a network error:"),
alloc
.string(reqwest_error.to_string())
.annotate(Annotation::PlainText)
.indent(4),
// TODO: What should the tip for HTTP IO errors be?
// Should we import reqwest and check stuff like
// reqwest_error.{ is_redirect(), is_status(), is_timeout(), ... } ?
//
// alloc.concat([
// alloc.tip(),
// alloc.reflow(r"Check the error message."),
// ]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "HTTP ERROR".to_string(),
severity: Severity::Fatal,
}
}
Problem::InvalidUrl(roc_packaging::https::UrlProblem::InvalidExtensionSuffix(
invalid_suffix,
)) => {
let (suffix_text, annotation_style) = if invalid_suffix.is_empty() {
(r"empty".to_string(), Annotation::PlainText)
} else {
(invalid_suffix, Annotation::Emphasized)
};
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.concat([
alloc.reflow(r"However, this file's extension ("),
alloc.string(suffix_text).annotate(annotation_style),
alloc.reflow(r") is not a supported extension."),
]),
alloc.concat([
alloc.reflow(r"The supported extensions are "),
alloc.keyword(r".tar"),
alloc.reflow(r", "),
alloc.keyword(r".tar.gz"),
alloc.reflow(r" and "),
alloc.keyword(r".tar.br"),
]),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "INVALID EXTENSION SUFFIX".to_string(),
severity: Severity::Fatal,
}
}
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MissingTarExt) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.concat([
alloc.reflow(r"However, this file's extension is not "),
alloc.keyword(r".tar"),
alloc.reflow(r"."),
]),
alloc.concat([
alloc.reflow(r"The supported extensions are "),
alloc.keyword(r".tar"),
alloc.reflow(r", "),
alloc.keyword(r".tar.gz"),
alloc.reflow(r" and "),
alloc.keyword(r".tar.br"),
]),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "INVALID EXTENSION".to_string(),
severity: Severity::Fatal,
}
}
Problem::InvalidUrl(roc_packaging::https::UrlProblem::InvalidFragment(
invalid_fragment,
)) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.concat([
alloc.reflow(r"However, this URL's fragment (the part after #) "),
alloc.reflow(r"is not valid. When present, the fragment must point to "),
alloc.reflow(r"an existing "),
alloc.keyword(r".roc"),
alloc.reflow(r" file inside the package. Also, the filename can't be empty, "),
alloc.reflow(r"so a fragment of #.roc would also not be valid. This is the "),
alloc.reflow(r"invalid fragment I encountered: "),
]),
alloc
.string(invalid_fragment)
.annotate(Annotation::Emphasized)
.indent(4),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Check that the fragment points to an existing "),
alloc.keyword(r".roc"),
alloc.reflow(r" file inside the package. You can download this package "),
alloc.reflow(r"and inspect it locally."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "INVALID FRAGMENT".to_string(),
severity: Severity::Fatal,
}
}
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MissingHash) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.concat([
alloc.reflow(r"I use a content hash to detect if the file might "),
alloc.reflow(r"have been tampered with. This could happen if "),
alloc.reflow(r"the server or domain have been compromised."),
]),
alloc.concat([
alloc.reflow(r"The way this works is that the name of the file "),
alloc.reflow(r"is the BLAKE3 hash of the contents of the "),
alloc.reflow(r"file itself. If someone would tamper with the file, "),
alloc.reflow(r"I could notify and protect you. However, I could "),
alloc.reflow(r"not find the expected hash on the URL above, "),
alloc.reflow(r"so I cannot apply this tamper-check."),
]),
alloc.concat([
alloc.tip(),
alloc
.reflow(r"Check that you have the correct URL for this package/platform. "),
alloc.reflow(r"Here is an example of how such a hash looks like: "),
alloc
.string(r"tE4xS_zLdmmxmHwHih9kHWQ7fsXtJr7W7h3425-eZFk".to_string())
.annotate(Annotation::Emphasized),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "MISSING PACKAGE HASH".to_string(),
severity: Severity::Fatal,
}
}
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MissingHttps) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.concat([
alloc.reflow(r"For your security, I will only attempt to download "),
alloc.reflow(r"files from servers which use the "),
alloc.keyword(r"https"),
alloc.reflow(r" protocol."),
]),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "HTTPS MANDATORY".to_string(),
severity: Severity::Fatal,
}
}
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MisleadingCharacter) => {
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.concat([
alloc.reflow(r"I have found one or more potentially misleading "),
alloc.reflow(r"characters in this URL. Misleading characters are "),
alloc.reflow(r"characters that look like others but aren't the same. "),
alloc.reflow(r"The following characters are classified as misleading: "),
alloc.keyword(r"@"),
alloc.reflow(r", "),
alloc.keyword("\u{2044}"),
alloc.reflow(r" (unicode 2044), "),
alloc.keyword("\u{2215}"),
alloc.reflow(r" (unicode 2215), "),
alloc.keyword("\u{FF0F}"),
alloc.reflow(r" (unicode FF0F) and "),
alloc.keyword("\u{29F8}"),
alloc.reflow(r" (unicode 29F8). "),
]),
alloc.concat([
alloc.reflow(r"If you have a use-case for any of these characters we "),
alloc.reflow(r"would like to hear about it. Reach out on "),
alloc
.string(r"https://github.com/roc-lang/roc/issues/5487".to_string())
.annotate(Annotation::Url),
]),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "MISLEADING CHARACTERS".to_string(),
severity: Severity::Fatal,
}
}
Problem::DownloadTooBig(content_len) => {
let nice_bytes = Byte::from_bytes(content_len.into())
.get_appropriate_unit(false)
.format(3);
let doc = alloc.stack([
alloc.reflow(r"I was trying to download this URL:"),
alloc
.string((&url).to_string())
.annotate(Annotation::Url)
.indent(4),
alloc.concat([
alloc.reflow(r"But the server stated this file is "),
alloc.string(nice_bytes).annotate(Annotation::Keyword),
alloc.reflow(r" in size. This is larger that the maximum size I can handle (around 32 GB)."),
]),
alloc.concat([
alloc.tip(),
alloc.reflow(r"Check that you have the correct URL for this package/platform. "),
alloc.reflow(r"If you do, you should contact the package/platform's author and "),
alloc.reflow(r"notify them about this issue."),
]),
]);
Report {
filename: "UNKNOWN.roc".into(),
doc,
title: "FILE TOO LARGE".to_string(),
severity: Severity::Fatal,
}
}
}
}
pub fn to_file_problem_report_string(filename: &Path, error: io::ErrorKind) -> String { pub fn to_file_problem_report_string(filename: &Path, error: io::ErrorKind) -> String {
let src_lines: Vec<&str> = Vec::new(); let src_lines: Vec<&str> = Vec::new();

View file

@ -10151,6 +10151,41 @@ In roc, functions are always written as a lambda, like{}
"### "###
); );
test_report!(
forgot_to_remove_underscore,
indoc!(
r#"
\_foo -> foo
"#
),
|golden| pretty_assertions::assert_eq!(
golden,
indoc!(
r###"── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
Nothing is named `foo` in this scope.
4 \_foo -> foo
^^^
There is an ignored identifier of a similar name here:
4 \_foo -> foo
^^^^
Did you mean to remove the leading underscore?
If not, did you mean one of these?
Box
Bool
U8
F64
"###
),
)
);
test_report!( test_report!(
call_with_underscore_identifier, call_with_underscore_identifier,
indoc!( indoc!(
@ -10162,17 +10197,102 @@ In roc, functions are always written as a lambda, like{}
), ),
|golden| pretty_assertions::assert_eq!( |golden| pretty_assertions::assert_eq!(
golden, golden,
&format!( indoc!(
r###"── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ r###"── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
Underscores are not allowed in identifier names: An underscore is being used as a variable here:
6 f 1 _ 1 6 f 1 _ 1
{} ^
I recommend using camelCase. It's the standard style in Roc code! An underscore can be used to ignore a value when pattern matching, but
"###, it cannot be used as a variable.
" " // TODO make the reporter not insert extraneous spaces here in the first place! "###
),
)
);
test_report!(
call_with_declared_identifier_starting_with_underscore,
indoc!(
r#"
f = \x, y, z -> x + y + z
\a, _b -> f a _b 1
"#
),
|golden| pretty_assertions::assert_eq!(
golden,
indoc!(
r###"── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
This variable's name starts with an underscore:
6 \a, _b -> f a _b 1
^^
But then it is used here:
6 \a, _b -> f a _b 1
^^
A variable's name can only start with an underscore if the variable is
unused. Since you are using this variable, you could remove the
underscore from its name in both places.
"###
),
)
);
test_report!(
call_with_undeclared_identifier_starting_with_underscore,
indoc!(
r#"
f = \x, y, z -> x + y + z
\a, _b -> f a _r 1
"#
),
|golden| pretty_assertions::assert_eq!(
golden,
indoc!(
r###"
SYNTAX PROBLEM /code/proj/Main.roc
This variable's name starts with an underscore:
6 \a, _b -> f a _r 1
^^
A variable's name can only start with an underscore if the variable is
unused. But it looks like the variable is being used here!
"###
),
)
);
test_report!(
underscore_in_middle_of_identifier,
indoc!(
r#"
f = \x, y, z -> x + y + z
\a, _b -> f a var_name 1
"#
),
|golden| pretty_assertions::assert_eq!(
golden,
indoc!(
r###"
SYNTAX PROBLEM /code/proj/Main.roc
Underscores are not allowed in identifier names:
6 \a, _b -> f a var_name 1
^^^^^^^^
I recommend using camelCase. It's the standard style in Roc code!
"###
), ),
) )
); );

View file

@ -36,7 +36,6 @@ extern "C" {
) -> *mut c_void; ) -> *mut c_void;
pub fn roc_dealloc(ptr: *mut c_void, alignment: u32); pub fn roc_dealloc(ptr: *mut c_void, alignment: u32);
pub fn roc_panic(c_ptr: *mut c_void, tag_id: u32); pub fn roc_panic(c_ptr: *mut c_void, tag_id: u32);
pub fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void;
pub fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void; pub fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void;
} }

View file

@ -45,12 +45,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[cfg(test)]
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[cfg(test)] #[cfg(test)]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {

View file

@ -67,10 +67,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -9,7 +9,7 @@ use core::mem::MaybeUninit;
use glue::Metadata; use glue::Metadata;
use roc_std::{RocDict, RocList, RocResult, RocStr}; use roc_std::{RocDict, RocList, RocResult, RocStr};
use std::borrow::{Borrow, Cow}; use std::borrow::{Borrow, Cow};
use std::ffi::{ OsStr}; use std::ffi::OsStr;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
@ -199,11 +199,6 @@ fn display_roc_fn(module_name: &str, fn_name: &str) -> String {
format!("\u{001B}[36m{module_name}\u{001B}[39m.{fn_name}") format!("\u{001B}[36m{module_name}\u{001B}[39m.{fn_name}")
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -81,10 +81,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -61,11 +61,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -136,10 +136,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -44,11 +44,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -196,11 +196,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -38,11 +38,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -42,11 +42,6 @@ void roc_dealloc(void *ptr, unsigned int alignment)
free(ptr); free(ptr);
} }
void *roc_memcpy(void *dest, const void *src, size_t n)
{
return memcpy(dest, src, n);
}
void *roc_memset(void *str, int c, size_t n) void *roc_memset(void *str, int c, size_t n)
{ {
return memset(str, c, n); return memset(str, c, n);

View file

@ -27,11 +27,6 @@ void roc_panic(void *ptr, unsigned int alignment)
napi_throw_error(napi_global_env, NULL, (char *)ptr); napi_throw_error(napi_global_env, NULL, (char *)ptr);
} }
void *roc_memcpy(void *dest, const void *src, size_t n)
{
return memcpy(dest, src, n);
}
void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); }
// Reference counting // Reference counting

View file

@ -33,10 +33,6 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr))); free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
} }
export fn roc_memcpy(dest: *anyopaque, src: *anyopaque, count: usize) callconv(.C) void {
_ = memcpy(dest, src, count);
}
// NOTE roc_panic is provided in the JS file, so it can throw an exception // NOTE roc_panic is provided in the JS file, so it can throw an exception
extern fn roc__mainForHost_1_exposed(*RocStr) void; extern fn roc__mainForHost_1_exposed(*RocStr) void;

View file

@ -30,10 +30,6 @@ void roc_panic(void* ptr, unsigned int alignment) {
exit(0); exit(0);
} }
void* roc_memcpy(void* dest, const void* src, size_t n) {
return memcpy(dest, src, n);
}
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); } void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
int roc_shm_open(char* name, int oflag, int mode) { int roc_shm_open(char* name, int oflag, int mode) {

View file

@ -44,11 +44,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)

View file

@ -33,10 +33,6 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr))); free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
} }
export fn roc_memcpy(dest: *anyopaque, src: *anyopaque, count: usize) callconv(.C) void {
_ = memcpy(dest, src, count);
}
// NOTE roc_panic is provided in the JS file, so it can throw an exception // NOTE roc_panic is provided in the JS file, so it can throw an exception
extern fn roc__mainForHost_1_exposed(*RocStr) void; extern fn roc__mainForHost_1_exposed(*RocStr) void;

View file

@ -67,10 +67,6 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }

View file

@ -27,11 +27,6 @@ __attribute__((noreturn)) void roc_panic(void *ptr, unsigned int alignment)
PyErr_SetString(PyExc_RuntimeError, (char *)ptr); PyErr_SetString(PyExc_RuntimeError, (char *)ptr);
} }
void *roc_memcpy(void *dest, const void *src, size_t n)
{
return memcpy(dest, src, n);
}
void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); }
// Reference counting // Reference counting

View file

@ -24,10 +24,6 @@ void roc_panic(void* ptr, unsigned int alignment) {
exit(0); exit(0);
} }
void* roc_memcpy(void* dest, const void* src, size_t n) {
return memcpy(dest, src, n);
}
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); } void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
int roc_shm_open(char* name, int oflag, int mode) { int roc_shm_open(char* name, int oflag, int mode) {

View file

@ -23,11 +23,6 @@ __attribute__((noreturn)) void roc_panic(void *ptr, unsigned int alignment)
rb_raise(rb_eException, "%s", (char *)ptr); rb_raise(rb_eException, "%s", (char *)ptr);
} }
void *roc_memcpy(void *dest, const void *src, size_t n)
{
return memcpy(dest, src, n);
}
void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); }
// Reference counting // Reference counting

View file

@ -24,10 +24,6 @@ void roc_panic(void* ptr, unsigned int alignment) {
exit(0); exit(0);
} }
void* roc_memcpy(void* dest, const void* src, size_t n) {
return memcpy(dest, src, n);
}
void* roc_memmove(void* dest, const void* src, size_t n){ void* roc_memmove(void* dest, const void* src, size_t n){
return memmove(dest, src, n); return memmove(dest, src, n);
} }

View file

@ -9,10 +9,10 @@ use std::os::raw::c_char;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use syntect::easy::HighlightLines; use syntect::easy::HighlightLines;
use syntect::highlighting::{Style, ThemeSet};
use syntect::html::{ClassStyle, ClassedHTMLGenerator};
use syntect::parsing::SyntaxSet; use syntect::parsing::SyntaxSet;
use syntect::highlighting::{ThemeSet, Style}; use syntect::util::LinesWithEndings;
use syntect::util::{LinesWithEndings};
use syntect::html::{ClassedHTMLGenerator, ClassStyle};
extern "C" { extern "C" {
#[link_name = "roc__transformFileContentForHost_1_exposed"] #[link_name = "roc__transformFileContentForHost_1_exposed"]
@ -98,15 +98,6 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
} }
} }
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(
dest: *mut c_void,
src: *const c_void,
bytes: usize,
) -> *mut c_void {
libc::memcpy(dest, src, bytes)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n) libc::memset(dst, c, n)
@ -216,8 +207,8 @@ fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Resul
// And track a little bit of state // And track a little bit of state
let mut in_code_block = false; let mut in_code_block = false;
let mut is_roc_code = false; let mut is_roc_code = false;
let syntax_set : syntect::parsing::SyntaxSet = SyntaxSet::load_defaults_newlines(); let syntax_set: syntect::parsing::SyntaxSet = SyntaxSet::load_defaults_newlines();
let theme_set : syntect::highlighting::ThemeSet = ThemeSet::load_defaults(); let theme_set: syntect::highlighting::ThemeSet = ThemeSet::load_defaults();
for event in parser { for event in parser {
match event { match event {
@ -227,23 +218,25 @@ fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Resul
.strip_prefix("roc!") .strip_prefix("roc!")
.expect("expected leading 'roc!'"); .expect("expected leading 'roc!'");
let highlighted_html = roc_highlight::highlight_roc_code_inline(stripped.to_string().as_str()); let highlighted_html =
roc_highlight::highlight_roc_code_inline(stripped.to_string().as_str());
parser_with_highlighting.push(pulldown_cmark::Event::Html( parser_with_highlighting.push(pulldown_cmark::Event::Html(
pulldown_cmark::CowStr::from(highlighted_html), pulldown_cmark::CowStr::from(highlighted_html),
)); ));
} else { } else {
let inline_code = pulldown_cmark::CowStr::from(format!("<code>{}</code>", code_str)); let inline_code =
parser_with_highlighting.push( pulldown_cmark::CowStr::from(format!("<code>{}</code>", code_str));
pulldown_cmark::Event::Html(inline_code) parser_with_highlighting.push(pulldown_cmark::Event::Html(inline_code));
);
} }
} }
pulldown_cmark::Event::Start(pulldown_cmark::Tag::CodeBlock(cbk)) => { pulldown_cmark::Event::Start(pulldown_cmark::Tag::CodeBlock(cbk)) => {
in_code_block = true; in_code_block = true;
is_roc_code = is_roc_code_block(&cbk); is_roc_code = is_roc_code_block(&cbk);
} }
pulldown_cmark::Event::End(pulldown_cmark::Tag::CodeBlock(pulldown_cmark::CodeBlockKind::Fenced(extention_str))) => { pulldown_cmark::Event::End(pulldown_cmark::Tag::CodeBlock(
pulldown_cmark::CodeBlockKind::Fenced(extention_str),
)) => {
if in_code_block { if in_code_block {
match replace_code_with_static_file(&code_to_highlight, input_file) { match replace_code_with_static_file(&code_to_highlight, input_file) {
None => {} None => {}
@ -263,13 +256,19 @@ fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Resul
if is_roc_code { if is_roc_code {
highlighted_html = roc_highlight::highlight_roc_code(&code_to_highlight) highlighted_html = roc_highlight::highlight_roc_code(&code_to_highlight)
} else if let Some(syntax) = syntax_set.find_syntax_by_token(&extention_str) { } else if let Some(syntax) = syntax_set.find_syntax_by_token(&extention_str) {
let mut h = HighlightLines::new(syntax, &theme_set.themes["base16-ocean.dark"]); let mut h =
HighlightLines::new(syntax, &theme_set.themes["base16-ocean.dark"]);
let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced); let mut html_generator = ClassedHTMLGenerator::new_with_class_style(
syntax,
&syntax_set,
ClassStyle::Spaced,
);
for line in LinesWithEndings::from(&code_to_highlight) { for line in LinesWithEndings::from(&code_to_highlight) {
html_generator.parse_html_for_line_which_includes_newline(line); html_generator.parse_html_for_line_which_includes_newline(line);
} }
highlighted_html = format!("<pre><samp>{}</pre></samp>", html_generator.finalize()) highlighted_html =
format!("<pre><samp>{}</pre></samp>", html_generator.finalize())
} else { } else {
highlighted_html = format!("<pre><samp>{}</pre></samp>", &code_to_highlight) highlighted_html = format!("<pre><samp>{}</pre></samp>", &code_to_highlight)
} }
@ -367,7 +366,6 @@ fn replace_code_with_static_file(code: &str, input_file: &Path) -> Option<String
match trimmed_code.strip_prefix("file:") { match trimmed_code.strip_prefix("file:") {
None => None, None => None,
Some(path) => { Some(path) => {
// File must be located in input folder or sub-directory // File must be located in input folder or sub-directory
if path.contains("../") { if path.contains("../") {
panic!("ERROR File must be located within the input diretory!"); panic!("ERROR File must be located within the input diretory!");
@ -378,7 +376,10 @@ fn replace_code_with_static_file(code: &str, input_file: &Path) -> Option<String
// Check file exists before opening // Check file exists before opening
match file_path.try_exists() { match file_path.try_exists() {
Err(_) | Ok(false) => { Err(_) | Ok(false) => {
panic!("ERROR File does not exist: \"{}\"", file_path.to_str().unwrap()); panic!(
"ERROR File does not exist: \"{}\"",
file_path.to_str().unwrap()
);
} }
Ok(true) => { Ok(true) => {
let vec_u8 = fs::read(file_path).ok()?; let vec_u8 = fs::read(file_path).ok()?;

View file

@ -28,10 +28,6 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr))); free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
} }
export fn roc_memcpy(dest: *anyopaque, src: *anyopaque, count: usize) callconv(.C) void {
_ = memcpy(dest, src, count);
}
export fn roc_panic(message: RocStr, tag_id: u32) callconv(.C) void { export fn roc_panic(message: RocStr, tag_id: u32) callconv(.C) void {
_ = tag_id; _ = tag_id;
const msg = @ptrCast([*:0]const u8, c_ptr); const msg = @ptrCast([*:0]const u8, c_ptr);

View file

@ -28,10 +28,6 @@ export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr))); free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
} }
export fn roc_memcpy(dest: *anyopaque, src: *anyopaque, count: usize) callconv(.C) void {
_ = memcpy(dest, src, count);
}
export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void { export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
_ = tag_id; _ = tag_id;
const msg = @ptrCast([*:0]const u8, c_ptr); const msg = @ptrCast([*:0]const u8, c_ptr);

View file

@ -1,3 +1,3 @@
[files] [files]
extend-exclude = ["crates/vendor/", "examples/static-site-gen/input/"] extend-exclude = ["crates/vendor/", "examples/static-site-gen/input/", "COPYRIGHT"]