From af866429504d5fa3f42f2e333572db0e2e572690 Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Tue, 10 Mar 2020 22:22:27 -0400 Subject: [PATCH 1/8] Build fixes, clean up warnings --- libeir_interpreter/src/term.rs | 5 ++--- libeir_ir/Cargo.toml | 2 +- libeir_ir/src/text/dot_printer.rs | 2 +- libeir_passes/Cargo.toml | 4 ++-- libeir_syntax_erl/Cargo.toml | 2 +- util/libeir_util_datastructures/Cargo.toml | 2 +- util/libeir_util_dot_graph/src/lib.rs | 9 ++++----- util/libeir_util_prof/Cargo.toml | 2 +- 8 files changed, 13 insertions(+), 15 deletions(-) diff --git a/libeir_interpreter/src/term.rs b/libeir_interpreter/src/term.rs index 34995ce..b355059 100644 --- a/libeir_interpreter/src/term.rs +++ b/libeir_interpreter/src/term.rs @@ -7,8 +7,7 @@ use libeir_intern::{ Symbol, LocalInternedString }; use libeir_ir::{ FunctionIdent, Block }; -use libeir_util_binary::{ BitRead }; -use libeir_util_binary::{ BitVec, BitSlice, copy as bit_copy }; +use libeir_util_binary::{ BitVec, BitSlice }; use ::num_bigint::BigInt; use ::num_traits::cast::ToPrimitive; @@ -560,7 +559,7 @@ impl Term { } } - pub fn to_doc(term: Rc) -> pretty::Doc<'static, pretty::BoxDoc<'static>> { + pub fn to_doc(_term: Rc) -> pretty::Doc<'static, pretty::BoxDoc<'static>> { unimplemented!() //use pretty::{ Doc }; //match &*term { diff --git a/libeir_ir/Cargo.toml b/libeir_ir/Cargo.toml index 1581db6..9e07ffb 100644 --- a/libeir_ir/Cargo.toml +++ b/libeir_ir/Cargo.toml @@ -23,7 +23,7 @@ lazy_static = "1.2.0" itertools = "0.8.0" cranelift-entity = "0.56.0" -bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } +bumpalo = { git = "https://github.com/bitwalker/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } petgraph = "0.4" diff --git a/libeir_ir/src/text/dot_printer.rs b/libeir_ir/src/text/dot_printer.rs index c543f86..f7a350a 100644 --- a/libeir_ir/src/text/dot_printer.rs +++ b/libeir_ir/src/text/dot_printer.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use crate::Function; use pretty::Arena; use super::printer as pr; -use super::printer::{FunctionFormatData, BlockFormatSink, FormatConfig, FormatState}; +use super::printer::{FunctionFormatData, FormatState}; use libeir_util_dot_graph::GraphPrinter; diff --git a/libeir_passes/Cargo.toml b/libeir_passes/Cargo.toml index d7a4834..f583bd7 100644 --- a/libeir_passes/Cargo.toml +++ b/libeir_passes/Cargo.toml @@ -17,13 +17,13 @@ cranelift-entity = "0.56.0" petgraph = "0.4" -bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } +bumpalo = { git = "https://github.com/bitwalker/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } fnv = "1.0.3" log = "0.4" -hashbrown = { git = "https://github.com/hansihe/hashbrown.git", features = ["raw", "nightly"] } +hashbrown = { git = "https://github.com/bitwalker/hashbrown.git", features = ["raw", "nightly"] } [dev-dependencies] simple_logger = "1.0" diff --git a/libeir_syntax_erl/Cargo.toml b/libeir_syntax_erl/Cargo.toml index 46ebddb..0541036 100644 --- a/libeir_syntax_erl/Cargo.toml +++ b/libeir_syntax_erl/Cargo.toml @@ -29,7 +29,7 @@ itertools = "0.8" lazy_static = "1.2" either = "1.5" -bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } +bumpalo = { git = "https://github.com/bitwalker/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } # [dependencies.rug] # version = "1.2" diff --git a/util/libeir_util_datastructures/Cargo.toml b/util/libeir_util_datastructures/Cargo.toml index abf72c7..caec897 100644 --- a/util/libeir_util_datastructures/Cargo.toml +++ b/util/libeir_util_datastructures/Cargo.toml @@ -9,4 +9,4 @@ license = "MIT OR Apache-2.0" [dependencies] cranelift-entity = "0.56.0" -hashbrown = { git = "https://github.com/hansihe/hashbrown.git", features = ["raw", "nightly"] } +hashbrown = { git = "https://github.com/bitwalker/hashbrown.git", features = ["raw", "nightly"] } diff --git a/util/libeir_util_dot_graph/src/lib.rs b/util/libeir_util_dot_graph/src/lib.rs index 254b8e6..7cedf81 100644 --- a/util/libeir_util_dot_graph/src/lib.rs +++ b/util/libeir_util_dot_graph/src/lib.rs @@ -1,7 +1,6 @@ #![feature(allocator_api)] use std::fmt::Display; -use std::fs::File; use std::fmt::{Write, Error}; use std::path::Path; @@ -84,9 +83,9 @@ impl Content for &str { pub struct StructuredNode { } impl StructuredNode { - pub fn text(&mut self, content: F) {} - pub fn vertical(&mut self, cells: &[()]) {} - pub fn horizontal(&mut self, cells: &[()]) {} + pub fn text(&mut self, _content: F) {} + pub fn vertical(&mut self, _cells: &[()]) {} + pub fn horizontal(&mut self, _cells: &[()]) {} } pub struct GraphPrinter { @@ -101,7 +100,7 @@ impl GraphPrinter { Self::with_out(String::new()) } - pub fn finish_run_dot(mut self, out: &Path) { + pub fn finish_run_dot(self, out: &Path) { let dot_res = self.finish().unwrap(); let dir = std::env::temp_dir(); diff --git a/util/libeir_util_prof/Cargo.toml b/util/libeir_util_prof/Cargo.toml index 19c83b8..0fe3258 100644 --- a/util/libeir_util_prof/Cargo.toml +++ b/util/libeir_util_prof/Cargo.toml @@ -8,4 +8,4 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } +bumpalo = { git = "https://github.com/bitwalker/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } From b2f754dce471ae7f95bb981f67ac3ef96ebd3f1d Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Sat, 14 Mar 2020 13:16:05 -0400 Subject: [PATCH 2/8] Fix formatting of match selector --- libeir_ir/src/text/printer/operation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libeir_ir/src/text/printer/operation.rs b/libeir_ir/src/text/printer/operation.rs index d06c134..bd3a8fb 100644 --- a/libeir_ir/src/text/printer/operation.rs +++ b/libeir_ir/src/text/printer/operation.rs @@ -155,7 +155,7 @@ where branches_formatted.push(formatted.indent(2)); } - let selector = self.value_use_to_doc(config, state, reads[0]); + let selector = self.value_use_to_doc(config, state, reads[1]); arena.nil() .append(arena.text("match")) From c3ca08a8613c119fcb567a6aa98df467eb812292 Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Mon, 16 Mar 2020 17:14:54 -0400 Subject: [PATCH 3/8] Print better information when validation pass panics --- libeir_passes/src/validate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libeir_passes/src/validate.rs b/libeir_passes/src/validate.rs index 2e5a848..327d57d 100644 --- a/libeir_passes/src/validate.rs +++ b/libeir_passes/src/validate.rs @@ -21,6 +21,6 @@ impl FunctionPass for ValidatePass { fn run_function_pass(&mut self, b: &mut FunctionBuilder) { self.err_buf.clear(); b.fun().validate(&mut self.err_buf); - assert!(self.err_buf.len() == 0); + assert_eq!(self.err_buf.len(), 0, "{:?}", &self.err_buf); } } From 5c6628a08497dc5e461357835c97c11a28c881c0 Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Thu, 19 Mar 2020 19:57:11 -0400 Subject: [PATCH 4/8] Add source span information to EIR Module --- libeir_ir/src/module.rs | 19 ++++++++++++++++++- libeir_syntax_erl/src/lower/mod.rs | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/libeir_ir/src/module.rs b/libeir_ir/src/module.rs index 4c39292..c67c0d6 100644 --- a/libeir_ir/src/module.rs +++ b/libeir_ir/src/module.rs @@ -3,6 +3,7 @@ use std::collections::BTreeMap; use cranelift_entity::{PrimaryMap, entity_impl}; +use libeir_diagnostics::ByteSpan; use libeir_intern::{Ident, Symbol}; use crate::{Function, FunctionIdent}; @@ -34,14 +35,25 @@ entity_impl!(FunctionIndex, "function_index"); #[derive(Debug)] pub struct Module { name: Ident, + span: ByteSpan, functions: PrimaryMap, name_map: BTreeMap<(Symbol, usize), FunctionIndex> } impl Module { pub fn new(name: Ident) -> Self { - Module { + Self { + name, + span: Default::default(), + functions: PrimaryMap::new(), + name_map: BTreeMap::new(), + } + } + + pub fn new_with_span(name: Ident, span: ByteSpan) -> Self { + Self { name, + span, functions: PrimaryMap::new(), name_map: BTreeMap::new(), } @@ -51,6 +63,10 @@ impl Module { self.name } + pub fn span(&self) -> ByteSpan { + self.span + } + pub fn add_function(&mut self, name: Ident, arity: usize) -> &mut FunctionDefinition { let ident = FunctionIdent { module: self.name, @@ -107,6 +123,7 @@ impl Clone for Module { } Self { name: self.name.clone(), + span: self.span, functions, name_map, } diff --git a/libeir_syntax_erl/src/lower/mod.rs b/libeir_syntax_erl/src/lower/mod.rs index 3b8cc97..6773b45 100644 --- a/libeir_syntax_erl/src/lower/mod.rs +++ b/libeir_syntax_erl/src/lower/mod.rs @@ -167,7 +167,7 @@ pub fn lower_module<'a>( // TODO sort functions for more deterministic compilation - let mut ir_module = IrModule::new(module.name); + let mut ir_module = IrModule::new_with_span(module.name, module.span); let mut ctx = LowerCtx { codemap, From 68dd0130ecdcaa2763254fe282e126b0c4f505ef Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Sun, 22 Mar 2020 02:48:57 -0400 Subject: [PATCH 5/8] Propagate more location information --- libeir_ir/src/algo/mangle/mod.rs | 10 ++- libeir_ir/src/function/builder/mod.rs | 15 +++- libeir_ir/src/function/builder/op.rs | 87 +++++++++++++------ libeir_ir/src/function/builder/primop.rs | 46 ++++++---- libeir_ir/src/function/mod.rs | 17 ++-- libeir_ir/src/function/value.rs | 8 +- libeir_ir/src/graph/block_graph.rs | 3 +- libeir_ir/src/graph/live_block_graph.rs | 3 +- libeir_ir/src/module.rs | 4 +- libeir_ir/src/pattern/mod.rs | 46 ++++++---- libeir_ir/src/text/ast/lower.rs | 31 +++---- libeir_passes/Cargo.toml | 1 + .../src/compile_pattern/lower_cfg.rs | 21 +++-- .../src/lower/exception_handler_stack.rs | 5 +- libeir_syntax_erl/src/lower/expr/binary.rs | 4 +- .../src/lower/expr/binary_expr.rs | 15 ++-- libeir_syntax_erl/src/lower/expr/case.rs | 18 ++-- libeir_syntax_erl/src/lower/expr/catch.rs | 28 +++--- .../src/lower/expr/comprehension.rs | 22 +++-- libeir_syntax_erl/src/lower/expr/map.rs | 12 +-- libeir_syntax_erl/src/lower/expr/mod.rs | 39 +++++---- libeir_syntax_erl/src/lower/expr/record.rs | 34 ++++---- libeir_syntax_erl/src/lower/mod.rs | 16 ++-- libeir_syntax_erl/src/lower/pattern/mod.rs | 21 +++-- .../src/lower/pattern/tree/lower.rs | 3 +- 25 files changed, 309 insertions(+), 200 deletions(-) diff --git a/libeir_ir/src/algo/mangle/mod.rs b/libeir_ir/src/algo/mangle/mod.rs index c00facb..ff83913 100644 --- a/libeir_ir/src/algo/mangle/mod.rs +++ b/libeir_ir/src/algo/mangle/mod.rs @@ -2,6 +2,8 @@ use std::collections::{ BTreeSet, BTreeMap }; use bumpalo::{Bump, collections::Vec as BVec}; +use libeir_diagnostics::DUMMY_SPAN; + use crate::{ Function, FunctionBuilder }; use crate::{ Block }; use crate::{ ValueKind }; @@ -354,6 +356,7 @@ impl Mangler { } let kind = val.map_fun(recv, |f, v| f.value_kind(v)); + let locs = val.map_fun(recv, |f, v| f.value_locations(v)); let dest: ToValue = match kind.inner() { ValueKind::Const(_) => { recv.map_const(val) @@ -364,6 +367,11 @@ impl Mangler { .map_fun(recv, |f, _v| *f.primop_kind(prim.inner())) .inner(); + let span = locs + .inner() + .map(|spans| spans.first().copied().unwrap_or(DUMMY_SPAN)) + .unwrap_or(DUMMY_SPAN); + let mut buf = BVec::new_in(bump); { let fun = prim.fun(recv); @@ -375,7 +383,7 @@ impl Mangler { *value = self.map(bump, recv, value_m).inner(); } - recv.to().prim_from_kind(kind, &buf).into() + recv.to().prim_from_kind(span, kind, &buf).into() }, ValueKind::Block(block) => { let block = val.new_with(block); diff --git a/libeir_ir/src/function/builder/mod.rs b/libeir_ir/src/function/builder/mod.rs index 34b1f05..58cce77 100644 --- a/libeir_ir/src/function/builder/mod.rs +++ b/libeir_ir/src/function/builder/mod.rs @@ -1,3 +1,5 @@ +use libeir_diagnostics::{ByteSpan, DUMMY_SPAN}; + use super::{ Block, Value, PrimOp, Const, Location }; use super::ValueKind; use super::{ PrimOpData, PrimOpKind }; @@ -120,7 +122,11 @@ impl<'a> FunctionBuilder<'a> { } let kind = self.fun().primop_kind(prim).clone(); - self.prim_from_kind(kind, &values) + let span = self.fun() + .value_locations(value) + .map(|spans| spans.first().copied().unwrap_or(DUMMY_SPAN)) + .unwrap_or(DUMMY_SPAN); + self.prim_from_kind(span, kind, &values) } _ => value, } @@ -201,6 +207,10 @@ impl<'a> FunctionBuilder<'a> { self.fun.block_insert() } + pub fn block_insert_with_span(&mut self, span: Option) -> Block { + self.fun.block_insert_with_span(span) + } + /// Inserts a new block and get its value pub fn block_insert_get_val(&mut self) -> (Block, Value) { let block = self.block_insert(); @@ -315,6 +325,7 @@ mod tests { use crate::FunctionIdent; use libeir_intern::Ident; + use libeir_diagnostics::DUMMY_SPAN; #[test] fn graph_impl() { @@ -323,7 +334,7 @@ mod tests { name: Ident::from_str("test"), arity: 1, }; - let mut fun = Function::new(ident); + let mut fun = Function::new(DUMMY_SPAN, ident); let mut b = fun.builder(); { diff --git a/libeir_ir/src/function/builder/op.rs b/libeir_ir/src/function/builder/op.rs index 1c49ca1..2507312 100644 --- a/libeir_ir/src/function/builder/op.rs +++ b/libeir_ir/src/function/builder/op.rs @@ -1,4 +1,5 @@ use libeir_intern::Symbol; +use libeir_diagnostics::ByteSpan; use cranelift_entity::{ EntityList }; @@ -28,6 +29,7 @@ impl<'a> FunctionBuilder<'a> { pub fn op_call_function_next<'b, V>( &'b mut self, + span: ByteSpan, block: Block, target: V, ret: Value, @@ -47,11 +49,13 @@ impl<'a> FunctionBuilder<'a> { data.reads.push(ret, &mut self.fun.pool.value); data.reads.push(thr, &mut self.fun.pool.value); data.reads.extend(args.iter().cloned(), &mut self.fun.pool.value); + data.location = self.fun.locations.location(None, None, None, span); self.graph_update_block(block); } pub fn op_call_function<'b, V>( &'b mut self, + span: ByteSpan, block: Block, target: V, args: &[Value], @@ -65,51 +69,53 @@ impl<'a> FunctionBuilder<'a> { self.block_arg_insert(thr); self.block_arg_insert(thr); - self.op_call_function_next(block, target, ret_val, thr_val, args); + self.op_call_function_next(span, block, target, ret_val, thr_val, args); (ret, thr) } - pub fn op_trace_capture_raw_next(&mut self, block: Block, next: Value) { + pub fn op_trace_capture_raw_next(&mut self, span: ByteSpan, block: Block, next: Value) { let data = self.fun.blocks.get_mut(block).unwrap(); assert!(data.op.is_none()); assert!(data.reads.is_empty()); data.op = Some(OpKind::TraceCaptureRaw); data.reads.push(next, &mut self.fun.pool.value); + data.location = self.fun.locations.location(None, None, None, span); self.graph_update_block(block); } - pub fn op_trace_capture_raw(&mut self, block: Block) -> Block { + pub fn op_trace_capture_raw(&mut self, span: ByteSpan, block: Block) -> Block { let cont = self.fun.block_insert(); let cont_val = self.value(cont); self.fun.block_arg_insert(cont); - self.op_trace_capture_raw_next(block, cont_val); + self.op_trace_capture_raw_next(span, block, cont_val); cont } - pub fn op_intrinsic<'b>(&'b mut self, block: Block, name: Symbol, args: &[Value]) { + pub fn op_intrinsic<'b>(&'b mut self, span: ByteSpan, block: Block, name: Symbol, args: &[Value]) { let data = self.fun.blocks.get_mut(block).unwrap(); assert!(data.op.is_none()); assert!(data.reads.is_empty()); data.op = Some(OpKind::Intrinsic(name)); data.reads.extend(args.iter().cloned(), &mut self.fun.pool.value); + data.location = self.fun.locations.location(None, None, None, span); self.graph_update_block(block); } - pub fn op_intrinsic_build(&mut self, name: Symbol) -> IntrinsicBuilder { - IntrinsicBuilder::new(name, self) + pub fn op_intrinsic_build(&mut self, span: ByteSpan, name: Symbol) -> IntrinsicBuilder { + IntrinsicBuilder::new(span, name, self) } - pub fn op_map_put_build(&mut self, value: Value) -> MapPutBuilder { - MapPutBuilder::new(value, self) + pub fn op_map_put_build(&mut self, span: ByteSpan, value: Value) -> MapPutBuilder { + MapPutBuilder::new(span, value, self) } - pub fn op_case_build(&mut self) -> CaseBuilder { - CaseBuilder::new() + pub fn op_case_build(&mut self, span: ByteSpan) -> CaseBuilder { + CaseBuilder::new(span) } pub fn op_unpack_value_list_next(&mut self, block: Block, target: Value, list: Value, num: usize) { @@ -133,7 +139,7 @@ impl<'a> FunctionBuilder<'a> { cont } - pub fn op_if_bool_next(&mut self, block: Block, t: Value, f: Value, o: Value, value: Value) { + pub fn op_if_bool_next(&mut self, span: ByteSpan, block: Block, t: Value, f: Value, o: Value, value: Value) { let data = self.fun.blocks.get_mut(block).unwrap(); assert!(data.op.is_none()); assert!(data.reads.is_empty()); @@ -143,10 +149,11 @@ impl<'a> FunctionBuilder<'a> { data.reads.push(f, &mut self.fun.pool.value); data.reads.push(o, &mut self.fun.pool.value); data.reads.push(value, &mut self.fun.pool.value); + data.location = self.fun.locations.location(None, None, None, span); self.graph_update_block(block); } - pub fn op_if_bool(&mut self, block: Block, value: Value) -> (Block, Block, Block) { + pub fn op_if_bool(&mut self, span: ByteSpan, block: Block, value: Value) -> (Block, Block, Block) { let true_cont = self.fun.block_insert(); let true_cont_val = self.value(true_cont); let false_cont = self.fun.block_insert(); @@ -154,12 +161,12 @@ impl<'a> FunctionBuilder<'a> { let non_cont = self.fun.block_insert(); let non_cont_val = self.value(non_cont); - self.op_if_bool_next(block, true_cont_val, false_cont_val, non_cont_val, value); + self.op_if_bool_next(span, block, true_cont_val, false_cont_val, non_cont_val, value); (true_cont, false_cont, non_cont) } - pub fn op_if_bool_strict_next(&mut self, block: Block, t: Value, f: Value, value: Value) { + pub fn op_if_bool_strict_next(&mut self, span: ByteSpan, block: Block, t: Value, f: Value, value: Value) { let data = self.fun.blocks.get_mut(block).unwrap(); assert!(data.op.is_none()); assert!(data.reads.is_empty()); @@ -168,21 +175,22 @@ impl<'a> FunctionBuilder<'a> { data.reads.push(t, &mut self.fun.pool.value); data.reads.push(f, &mut self.fun.pool.value); data.reads.push(value, &mut self.fun.pool.value); + data.location = self.fun.locations.location(None, None, None, span); self.graph_update_block(block); } - pub fn op_if_bool_strict(&mut self, block: Block, value: Value) -> (Block, Block) { + pub fn op_if_bool_strict(&mut self, span: ByteSpan, block: Block, value: Value) -> (Block, Block) { let true_cont = self.fun.block_insert(); let true_cont_val = self.value(true_cont); let false_cont = self.fun.block_insert(); let false_cont_val = self.value(false_cont); - self.op_if_bool_strict_next(block, true_cont_val, false_cont_val, value); + self.op_if_bool_strict_next(span, block, true_cont_val, false_cont_val, value); (true_cont, false_cont) } - pub fn op_binary_push(&mut self, block: Block, specifier: BinaryEntrySpecifier, + pub fn op_binary_push(&mut self, span: ByteSpan, block: Block, specifier: BinaryEntrySpecifier, bin: Value, value: Value, size: Option) -> (Block, Block) { if size.is_some() { assert!(specifier.has_size()); @@ -208,6 +216,7 @@ impl<'a> FunctionBuilder<'a> { if let Some(size) = size { data.reads.push(size, &mut self.fun.pool.value); } + data.location = self.fun.locations.location(None, None, None, span); self.graph_update_block(block); @@ -215,32 +224,35 @@ impl<'a> FunctionBuilder<'a> { } - pub fn op_unreachable(&mut self, block: Block) { + pub fn op_unreachable(&mut self, span: ByteSpan, block: Block) { let data = self.fun.blocks.get_mut(block).unwrap(); assert!(data.op.is_none()); assert!(data.reads.is_empty()); data.op = Some(OpKind::Unreachable); + data.location = self.fun.locations.location(None, None, None, span); self.graph_update_block(block); } - pub fn op_match_build(&mut self) -> MatchBuilder { - MatchBuilder::new() + pub fn op_match_build(&mut self, span: ByteSpan) -> MatchBuilder { + MatchBuilder::new(span) } } pub struct IntrinsicBuilder { name: Symbol, + span: ByteSpan, pub block: Option, values: EntityList, } impl IntrinsicBuilder { - pub fn new<'a>(name: Symbol, _b: &mut FunctionBuilder<'a>) -> Self { + pub fn new<'a>(span: ByteSpan, name: Symbol, _b: &mut FunctionBuilder<'a>) -> Self { IntrinsicBuilder { name, + span, block: None, values: EntityList::new(), } @@ -264,6 +276,7 @@ impl IntrinsicBuilder { data.op = Some(OpKind::Intrinsic(self.name)); data.reads = self.values; + data.location = b.fun.locations.location(None, None, None, self.span); b.graph_update_block(block); } @@ -271,6 +284,8 @@ impl IntrinsicBuilder { } pub struct CaseBuilder { + span: ByteSpan, + pub match_on: Option, pub no_match: Option, @@ -282,6 +297,7 @@ pub struct CaseBuilder { impl Default for CaseBuilder { fn default() -> Self { CaseBuilder { + span: libeir_diagnostics::DUMMY_SPAN, match_on: None, no_match: None, @@ -294,8 +310,10 @@ impl Default for CaseBuilder { impl CaseBuilder { - pub fn new() -> Self { - Self::default() + pub fn new(span: ByteSpan) -> Self { + let mut this = Self::default(); + this.span = span; + this } pub fn push_clause<'a>(&mut self, clause: PatternClause, guard: Value, body: Value, b: &mut FunctionBuilder<'a>) { @@ -324,6 +342,7 @@ impl CaseBuilder { data.op = Some(OpKind::Case { clauses: self.clauses, }); + data.location = b.fun.locations.location(None, None, None, self.span); let mut buf = b.value_buf.take().unwrap(); buf.clear(); @@ -359,6 +378,7 @@ impl CaseBuilder { } pub struct MatchBuilder { + span: ByteSpan, branches: EntityList, branch_args: EntityList, @@ -367,6 +387,7 @@ pub struct MatchBuilder { impl Default for MatchBuilder { fn default() -> Self { MatchBuilder { + span: libeir_diagnostics::DUMMY_SPAN, branches: EntityList::new(), branch_args: EntityList::new(), @@ -376,8 +397,10 @@ impl Default for MatchBuilder { } impl MatchBuilder { - pub fn new() -> Self { - Self::default() + pub fn new(span: ByteSpan) -> Self { + let mut this = Self::default(); + this.span = span; + this } pub fn push_value_next(&mut self, next: Value, val: Value, b: &mut FunctionBuilder) { @@ -490,8 +513,12 @@ impl MatchBuilder { let args = b.prim_value_list(&[]); self.branch_args.push(args, &mut b.fun.pool.value); } - pub fn push_wildcard(&mut self, b: &mut FunctionBuilder) -> Block { + pub fn push_wildcard(&mut self, span: ByteSpan, b: &mut FunctionBuilder) -> Block { let (block, block_val) = b.block_insert_get_val(); + { + let mut block_data = b.fun.blocks.get_mut(block).unwrap(); + block_data.location = b.fun.locations.location(None, None, None, span); + } self.push_wildcard_next(block_val, b); block } @@ -517,6 +544,7 @@ impl MatchBuilder { branches: self.kinds, }); data.reads = reads; + data.location = b.fun.locations.location(None, None, None, self.span); b.graph_update_block(block); b.fun.graph_validate_block(block); @@ -525,6 +553,7 @@ impl MatchBuilder { } pub struct MapPutBuilder { + span: ByteSpan, ok: Block, fail: Block, reads: EntityList, @@ -532,7 +561,7 @@ pub struct MapPutBuilder { } impl MapPutBuilder { - pub fn new(value: Value, b: &mut FunctionBuilder) -> Self { + pub fn new(span: ByteSpan, value: Value, b: &mut FunctionBuilder) -> Self { let (ok, ok_val) = b.block_insert_get_val(); b.block_arg_insert(ok); @@ -546,6 +575,7 @@ impl MapPutBuilder { reads.push(value, &mut b.fun.pool.value); MapPutBuilder { + span, ok, fail, reads, @@ -569,6 +599,7 @@ impl MapPutBuilder { data.op = Some(OpKind::MapPut { action: self.actions }); data.reads = self.reads; + data.location = b.fun.locations.location(None, None, None, self.span); b.graph_update_block(block); diff --git a/libeir_ir/src/function/builder/primop.rs b/libeir_ir/src/function/builder/primop.rs index 4a356b8..fee43d5 100644 --- a/libeir_ir/src/function/builder/primop.rs +++ b/libeir_ir/src/function/builder/primop.rs @@ -1,13 +1,15 @@ use ::cranelift_entity::{ EntityList }; -use crate::{ Value, ValueKind, LogicOp, IntoValue }; -use crate::ConstKind; +use libeir_diagnostics::ByteSpan; + +use crate::{ Value, ValueKind, ConstKind, LogicOp, IntoValue }; use super::{ FunctionBuilder, PrimOpData, PrimOpKind, BinOp }; /// PrimOp constructors impl<'a> FunctionBuilder<'a> { - pub fn prim_binop(&mut self, op: BinOp, lhs: Value, rhs: Value) -> Value { + pub fn prim_binop(&mut self, span: ByteSpan, op: BinOp, lhs: Value, rhs: Value) -> Value { + let loc = self.fun.locations.location(None, None, None, span); let mut reads = EntityList::new(); if op.symmetric() && lhs >= rhs { reads.extend([rhs, lhs].iter().cloned(), &mut self.fun.pool.value); @@ -19,13 +21,13 @@ impl<'a> FunctionBuilder<'a> { op: PrimOpKind::BinOp(op), reads, }, &self.fun.pool); - self.fun.values.push(ValueKind::PrimOp(primop)) + self.fun.values.push_with_location(ValueKind::PrimOp(primop), Some(loc)) } /// This will construct a tuple. /// If all values are constants, a constant tuple is created. /// Otherwise, a tuple PrimOp is created. - pub fn prim_tuple(&mut self, values: &[Value]) -> Value { + pub fn prim_tuple(&mut self, span: ByteSpan, values: &[Value]) -> Value { if values.iter().all(|v| self.fun.value_const(*v).is_some()) { let mut entries = EntityList::new(); for val in values { @@ -38,6 +40,7 @@ impl<'a> FunctionBuilder<'a> { }); self.value(cons) } else { + let loc = self.fun.locations.location(None, None, None, span); let mut reads = EntityList::new(); for val in values { reads.push(*val, &mut self.fun.pool.value); @@ -47,14 +50,14 @@ impl<'a> FunctionBuilder<'a> { op: PrimOpKind::Tuple, reads, }, &self.fun.pool); - self.fun.values.push(ValueKind::PrimOp(primop)) + self.fun.values.push_with_location(ValueKind::PrimOp(primop), Some(loc)) } } /// This will construct a new map. /// If all values are constants, a constant map is created. /// Otherwise, a map PrimOp is created. - pub fn prim_map(&mut self, keys: &[Value], values: &[Value]) -> Value { + pub fn prim_map(&mut self, span: ByteSpan, keys: &[Value], values: &[Value]) -> Value { if keys.iter().all(|v| self.fun.value_const(*v).is_some()) && values.iter().all(|v| self.fun.value_const(*v).is_some()) { @@ -80,6 +83,7 @@ impl<'a> FunctionBuilder<'a> { let cons = self.cons_mut().from(ConstKind::Map { keys: key_list, values: val_list }); self.value(cons) } else { + let loc = self.fun.locations.location(None, None, None, span); let mut value_pair = self.value_pair_buf.take().unwrap(); assert!(value_pair.is_empty()); @@ -100,11 +104,11 @@ impl<'a> FunctionBuilder<'a> { op: PrimOpKind::Map, reads: entries_list, }, &self.fun.pool); - self.fun.values.push(ValueKind::PrimOp(primop)) + self.fun.values.push_with_location(ValueKind::PrimOp(primop), Some(loc)) } } - pub fn prim_list_cell(&mut self, head: Value, tail: Value) -> Value { + pub fn prim_list_cell(&mut self, span: ByteSpan, head: Value, tail: Value) -> Value { if let (Some(head_v), Some(tail_v)) = (self.fun.value_const(head), self.fun.value_const(tail)) { let cons = self.cons_mut().from(ConstKind::ListCell { head: head_v, @@ -112,6 +116,7 @@ impl<'a> FunctionBuilder<'a> { }); self.value(cons) } else { + let loc = self.fun.locations.location(None, None, None, span); let mut entries_list = EntityList::new(); entries_list.push(head, &mut self.fun.pool.value); entries_list.push(tail, &mut self.fun.pool.value); @@ -120,7 +125,7 @@ impl<'a> FunctionBuilder<'a> { op: PrimOpKind::ListCell, reads: entries_list, }, &self.fun.pool); - self.fun.values.push(ValueKind::PrimOp(primop)) + self.fun.values.push_with_location(ValueKind::PrimOp(primop), Some(loc)) } } @@ -167,7 +172,7 @@ impl<'a> FunctionBuilder<'a> { self.fun.values.push(ValueKind::PrimOp(primop)) } - pub fn prim_logic_op(&mut self, op: LogicOp, values: &[Value]) -> Value { + pub fn prim_logic_op(&mut self, span: ByteSpan, op: LogicOp, values: &[Value]) -> Value { match (op, values.len()) { (LogicOp::And, 0) => return self.value(true), (LogicOp::And, 1) => return values[0], @@ -209,6 +214,7 @@ impl<'a> FunctionBuilder<'a> { } } } else { + let loc = self.fun.locations.location(None, None, None, span); let mut entries_list = EntityList::new(); entries_list.extend(values.iter().cloned(), &mut self.fun.pool.value); @@ -216,12 +222,13 @@ impl<'a> FunctionBuilder<'a> { op: PrimOpKind::LogicOp(op), reads: entries_list, }, &self.fun.pool); - self.fun.values.push(ValueKind::PrimOp(primop)) + self.fun.values.push_with_location(ValueKind::PrimOp(primop), Some(loc)) } } pub fn prim_capture_function( &mut self, + span: ByteSpan, m: M, f: F, a: A) -> Value where M: IntoValue, F: IntoValue, A: IntoValue, @@ -235,35 +242,36 @@ impl<'a> FunctionBuilder<'a> { entries_list.push(f_val, &mut self.fun.pool.value); entries_list.push(a_val, &mut self.fun.pool.value); + let loc = self.fun.locations.location(None, None, None, span); let primop = self.fun.primops.push(PrimOpData { op: PrimOpKind::CaptureFunction, reads: entries_list, }, &self.fun.pool); - self.fun.values.push(ValueKind::PrimOp(primop)) + self.fun.values.push_with_location(ValueKind::PrimOp(primop), Some(loc)) } - pub fn prim_from_kind(&mut self, op: PrimOpKind, vals: &[Value]) -> Value { + pub fn prim_from_kind(&mut self, span: ByteSpan, op: PrimOpKind, vals: &[Value]) -> Value { match op { PrimOpKind::ValueList => { self.prim_value_list(vals) } PrimOpKind::Tuple => { - self.prim_tuple(vals) + self.prim_tuple(span, vals) } PrimOpKind::CaptureFunction => { assert!(vals.len() == 3); - self.prim_capture_function(vals[0], vals[1], vals[2]) + self.prim_capture_function(span, vals[0], vals[1], vals[2]) } PrimOpKind::LogicOp(op) => { - self.prim_logic_op(op, vals) + self.prim_logic_op(span, op, vals) } PrimOpKind::BinOp(op) => { assert!(vals.len() == 2); - self.prim_binop(op, vals[0], vals[1]) + self.prim_binop(span, op, vals[0], vals[1]) } PrimOpKind::ListCell => { assert!(vals.len() == 2); - self.prim_list_cell(vals[0], vals[1]) + self.prim_list_cell(span, vals[0], vals[1]) } p => unimplemented!("{:?}", p), } diff --git a/libeir_ir/src/function/mod.rs b/libeir_ir/src/function/mod.rs index d81a8f4..9428e1b 100644 --- a/libeir_ir/src/function/mod.rs +++ b/libeir_ir/src/function/mod.rs @@ -10,7 +10,7 @@ use libeir_util_datastructures::pooled_entity_set::{ EntitySetPool, EntitySet, use libeir_util_datastructures::aux_hash_map::{ AuxHash, AuxEq }; use libeir_util_datastructures::dedup_aux_primary_map::DedupAuxPrimaryMap; -use libeir_diagnostics::{ ByteSpan, DUMMY_SPAN }; +use libeir_diagnostics::ByteSpan; use crate::{ FunctionIdent }; use crate::constant::{ ConstantContainer, Const, ConstKind }; @@ -318,7 +318,15 @@ impl Function { /// Blocks impl Function { + #[inline(always)] fn block_insert(&mut self) -> Block { + self.block_insert_with_span(None) + } + + fn block_insert_with_span(&mut self, span: Option) -> Block { + let location = span + .map(|s| self.locations.location(None, None, None, s)) + .unwrap_or_else(|| self.locations.location_empty()); let block = self.blocks.push(BlockData { arguments: EntityList::new(), @@ -328,7 +336,7 @@ impl Function { predecessors: EntitySet::new(), successors: EntitySet::new(), - location: self.locations.location_empty(), + location, }); self.values.push(ValueKind::Block(block)); block @@ -421,7 +429,6 @@ impl Function { pub fn block_iter(&self) -> impl Iterator { self.blocks.keys() } - } /// Graph @@ -508,10 +515,10 @@ impl SetPoolProvider for Block { impl Function { - pub fn new(ident: FunctionIdent) -> Self { + pub fn new(span: ByteSpan, ident: FunctionIdent) -> Self { Function { ident, - span: DUMMY_SPAN, + span, blocks: PrimaryMap::new(), values: ValueMap::new(), diff --git a/libeir_ir/src/function/value.rs b/libeir_ir/src/function/value.rs index 32ac389..27a0ec5 100644 --- a/libeir_ir/src/function/value.rs +++ b/libeir_ir/src/function/value.rs @@ -45,7 +45,6 @@ pub struct ValueMap { } impl ValueMap { - pub fn new() -> Self { ValueMap { primary: PrimaryMap::new(), @@ -54,12 +53,16 @@ impl ValueMap { } pub fn push(&mut self, kind: ValueKind) -> Value { + self.push_with_location(kind, None) + } + + pub fn push_with_location(&mut self, kind: ValueKind, location: Option) -> Value { if let Some(key) = self.back.get(&kind) { *key } else { let val = self.primary.push(ValueData { kind, - location: None, + location, usages: EntitySet::new(), }); self.back.insert(kind, val); @@ -70,7 +73,6 @@ impl ValueMap { pub fn get(&self, kind: ValueKind) -> Option { self.back.get(&kind).cloned() } - } impl Index for ValueMap { diff --git a/libeir_ir/src/graph/block_graph.rs b/libeir_ir/src/graph/block_graph.rs index c2d2afb..f602dc0 100644 --- a/libeir_ir/src/graph/block_graph.rs +++ b/libeir_ir/src/graph/block_graph.rs @@ -151,6 +151,7 @@ mod tests { use crate::{ FunctionIdent, Function, FunctionBuilder }; use libeir_intern::Ident; + use libeir_diagnostics::DUMMY_SPAN; use petgraph::Direction; use petgraph::visit::IntoNeighborsDirected; @@ -162,7 +163,7 @@ mod tests { name: Ident::from_str("woo"), arity: 1, }; - let mut fun = Function::new(ident); + let mut fun = Function::new(DUMMY_SPAN, ident); let mut b = FunctionBuilder::new(&mut fun); let b1 = b.block_insert(); diff --git a/libeir_ir/src/graph/live_block_graph.rs b/libeir_ir/src/graph/live_block_graph.rs index e1b2a23..ce223c3 100644 --- a/libeir_ir/src/graph/live_block_graph.rs +++ b/libeir_ir/src/graph/live_block_graph.rs @@ -134,6 +134,7 @@ mod tests { use crate::{ FunctionIdent, Function, FunctionBuilder }; use libeir_intern::Ident; + use libeir_diagnostics::DUMMY_SPAN; use petgraph::Direction; use petgraph::visit::IntoNeighborsDirected; @@ -145,7 +146,7 @@ mod tests { name: Ident::from_str("woo"), arity: 1, }; - let mut fun = Function::new(ident); + let mut fun = Function::new(DUMMY_SPAN, ident); let mut b = FunctionBuilder::new(&mut fun); let b1 = b.block_insert(); diff --git a/libeir_ir/src/module.rs b/libeir_ir/src/module.rs index c67c0d6..e6db2c0 100644 --- a/libeir_ir/src/module.rs +++ b/libeir_ir/src/module.rs @@ -67,7 +67,7 @@ impl Module { self.span } - pub fn add_function(&mut self, name: Ident, arity: usize) -> &mut FunctionDefinition { + pub fn add_function(&mut self, span: ByteSpan, name: Ident, arity: usize) -> &mut FunctionDefinition { let ident = FunctionIdent { module: self.name, name, @@ -75,7 +75,7 @@ impl Module { }; assert!(!self.name_map.contains_key(&(name.name, arity))); - let fun = Function::new(ident); + let fun = Function::new(span, ident); let def = FunctionDefinition { index: FunctionIndex(0), fun, diff --git a/libeir_ir/src/pattern/mod.rs b/libeir_ir/src/pattern/mod.rs index 5da5d6e..88bb549 100644 --- a/libeir_ir/src/pattern/mod.rs +++ b/libeir_ir/src/pattern/mod.rs @@ -53,6 +53,7 @@ impl Default for PatternContainer { #[derive(Debug, Clone)] struct PatternClauseData { + span: ByteSpan, root_nodes: EntityList, node_binds_keys: EntityList, @@ -144,11 +145,11 @@ impl PatternContainer { val } - pub fn node_empty(&mut self) -> PatternNode { + pub fn node_empty(&mut self, span: Option) -> PatternNode { self.nodes.push(PatternNodeData { kind: None, finished: false, - span: DUMMY_SPAN, + span: span.unwrap_or(DUMMY_SPAN), }) } @@ -242,8 +243,10 @@ impl PatternContainer { self.nodes[node].span = span; } - pub fn clause_start(&mut self) -> PatternClause { + pub fn clause_start(&mut self, span: ByteSpan) -> PatternClause { self.clauses.push(PatternClauseData { + span, + root_nodes: EntityList::new(), node_binds_keys: EntityList::new(), @@ -387,19 +390,19 @@ fn copy_pattern_node( let data = &from.nodes[node]; match data.kind.as_ref().unwrap() { PatternNodeKind::Wildcard => { - let new = to.node_empty(); + let new = to.node_empty(Some(data.span)); to.wildcard(new); node_map.insert(node, new); new }, PatternNodeKind::Value(val) => { - let new = to.node_empty(); + let new = to.node_empty(Some(data.span)); to.value(new, *val); node_map.insert(node, new); new } PatternNodeKind::Tuple(elems) => { - let new = to.node_empty(); + let new = to.node_empty(Some(data.span)); to.tuple(new); for elem in elems.as_slice(&from.node_pool) { @@ -415,14 +418,14 @@ fn copy_pattern_node( let head_copied = copy_pattern_node(value_map, node_map, *head, from, to); let tail_copied = copy_pattern_node(value_map, node_map, *tail, from, to); - let new = to.node_empty(); + let new = to.node_empty(Some(data.span)); to.list(new, head_copied, tail_copied); node_map.insert(node, new); new } PatternNodeKind::Map { keys, values } => { - let new = to.node_empty(); + let new = to.node_empty(Some(data.span)); to.map(new); for (key, val) in keys.as_slice(&from.value_pool).iter() @@ -456,13 +459,15 @@ impl<'a> FunctionBuilder<'a> { let rhs_kind = self.pat().nodes[rhs].kind.clone().unwrap(); println!("MERGE {:?} {:?}", lhs_kind, rhs_kind); + // TODO: Fuse locations + let fused_span = self.pat().nodes[lhs].span; match (lhs_kind, rhs_kind) { (PatternNodeKind::Value(l_val), PatternNodeKind::Value(r_val)) if l_val == r_val => { map.insert(rhs, lhs); Ok(lhs) } - (PatternNodeKind::Const(l), _) => self.merge_pattern_constant(map, rhs, l), - (_, PatternNodeKind::Const(r)) => self.merge_pattern_constant(map, lhs, r), + (PatternNodeKind::Const(l), _) => self.merge_pattern_constant(map, fused_span, rhs, l), + (_, PatternNodeKind::Const(r)) => self.merge_pattern_constant(map, fused_span, lhs, r), (PatternNodeKind::Wildcard, _) => { map.insert(lhs, rhs); Ok(rhs) @@ -475,7 +480,7 @@ impl<'a> FunctionBuilder<'a> { let l1_len = l1.len(&self.pat().node_pool); let l2_len = l2.len(&self.pat().node_pool); if l1_len == l2_len { - let new_tup = self.pat_mut().node_empty(); + let new_tup = self.pat_mut().node_empty(Some(fused_span)); self.pat_mut().tuple(new_tup); for idx in 0..l1_len { let ln = l1.get(idx, &self.pat().node_pool).unwrap(); @@ -499,7 +504,7 @@ impl<'a> FunctionBuilder<'a> { } } (PatternNodeKind::Map { keys: k1, values: v1 }, PatternNodeKind::Map { keys: k2, values: v2 }) => { - let new_map = self.pat_mut().node_empty(); + let new_map = self.pat_mut().node_empty(Some(fused_span)); self.pat_mut().map(new_map); for idx in 0..(k1.len(&self.pat().value_pool)) { @@ -524,7 +529,7 @@ impl<'a> FunctionBuilder<'a> { let new_head = self.merge_patterns(map, h1, h2)?; let new_tail = self.merge_patterns(map, t1, t2)?; - let list = self.pat_mut().node_empty(); + let list = self.pat_mut().node_empty(Some(fused_span)); self.pat_mut().list(list, new_head, new_tail); map.insert(lhs, list); @@ -546,6 +551,7 @@ impl<'a> FunctionBuilder<'a> { pub fn merge_pattern_constant( &mut self, map: &mut HashMap, + fused_span: ByteSpan, lhs: PatternNode, rhs: Const, ) -> Result @@ -566,7 +572,7 @@ impl<'a> FunctionBuilder<'a> { }) } (PatternNodeKind::Wildcard, _) => { - let node = self.pat_mut().node_empty(); + let node = self.pat_mut().node_empty(Some(fused_span)); self.pat_mut().constant(node, rhs); map.insert(lhs, node); Ok(node) @@ -583,7 +589,7 @@ impl<'a> FunctionBuilder<'a> { }); } - let node = self.pat_mut().node_empty(); + let node = self.pat_mut().node_empty(Some(fused_span)); self.pat_mut().tuple(node); for n in 0..pat_len { @@ -592,7 +598,9 @@ impl<'a> FunctionBuilder<'a> { let const_node = const_entries .get(n, &self.cons().const_pool).unwrap(); - let new = self.merge_pattern_constant(map, pat_node, const_node)?; + // TODO: Fuse locations + let fspan = self.pat().nodes[pat_node].span; + let new = self.merge_pattern_constant(map, fspan, pat_node, const_node)?; self.pat_mut().tuple_elem_push(node, new); } @@ -609,10 +617,10 @@ impl<'a> FunctionBuilder<'a> { } (PatternNodeKind::List { head: pat_head, tail: pat_tail }, ConstKind::ListCell { head: cons_head, tail: cons_tail }) => { - let head = self.merge_pattern_constant(map, pat_head, cons_head)?; - let tail = self.merge_pattern_constant(map, pat_tail, cons_tail)?; + let head = self.merge_pattern_constant(map, fused_span, pat_head, cons_head)?; + let tail = self.merge_pattern_constant(map, fused_span, pat_tail, cons_tail)?; - let node = self.pat_mut().node_empty(); + let node = self.pat_mut().node_empty(Some(fused_span)); self.pat_mut().list(node, head, tail); map.insert(lhs, node); diff --git a/libeir_ir/src/text/ast/lower.rs b/libeir_ir/src/text/ast/lower.rs index 01ed461..9a4d942 100644 --- a/libeir_ir/src/text/ast/lower.rs +++ b/libeir_ir/src/text/ast/lower.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use libeir_util_datastructures::hashmap_stack::HashMapStack; use libeir_intern::Ident; -use libeir_diagnostics::{ByteSpan, Diagnostic, Label}; +use libeir_diagnostics::{ByteSpan, DUMMY_SPAN, Diagnostic, Label}; use libeir_util_parse::{ErrorReceiver, ToDiagnostic}; use libeir_util_number::ToPrimitive; @@ -109,6 +109,7 @@ impl ast::Module { match item { ast::ModuleItem::Function(fun) => { let fun_ir = module.add_function( + DUMMY_SPAN, fun.name, fun.arity.to_usize().unwrap(), ); @@ -156,7 +157,7 @@ impl ast::Function { name: self.name, arity: self.arity.to_usize().unwrap(), }; - let mut fun = Function::new(ident); + let mut fun = Function::new(DUMMY_SPAN, ident); let mut b = fun.builder(); let map = self.lower_into(errors, &mut b)?; @@ -287,7 +288,7 @@ fn lower_operation( let args: Result, _> = call.args.iter() .map(|v| lower_value(errors, b, scope, v)) .collect(); - b.op_call_function_next(block, target, ret, thr, &args?); + b.op_call_function_next(DUMMY_SPAN, block, target, ret, thr, &args?); } ast::Op::UnpackValueList(list) => { let target = lower_value(errors, b, scope, &list.block)?; @@ -300,17 +301,17 @@ fn lower_operation( let fal = lower_value(errors, b, scope, &if_bool.fal)?; if let Some(or) = &if_bool.or { let or = lower_value(errors, b, scope, or)?; - b.op_if_bool_next(block, tru, fal, or, value); + b.op_if_bool_next(DUMMY_SPAN, block, tru, fal, or, value); } else { - b.op_if_bool_strict_next(block, tru, fal, value); + b.op_if_bool_strict_next(DUMMY_SPAN, block, tru, fal, value); } } ast::Op::TraceCaptureRaw(trace_op) => { let then = lower_value(errors, b, scope, &trace_op.then)?; - b.op_trace_capture_raw_next(block, then); + b.op_trace_capture_raw_next(DUMMY_SPAN, block, then); } ast::Op::Match(match_op) => { - let mut builder = b.op_match_build(); + let mut builder = b.op_match_build(DUMMY_SPAN); for entry in match_op.entries.iter() { let next = lower_value(errors, b, scope, &entry.target)?; match &entry.kind { @@ -342,7 +343,7 @@ fn lower_operation( builder.finish(block, match_val, b); } ast::Op::Unreachable => { - b.op_unreachable(block); + b.op_unreachable(DUMMY_SPAN, block); } ast::Op::Case(case_op) => { let value = lower_value( @@ -350,10 +351,10 @@ fn lower_operation( let mut binds = HashMap::new(); - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(DUMMY_SPAN); for entry in case_op.entries.iter() { binds.clear(); - let clause = b.pat_mut().clause_start(); + let clause = b.pat_mut().clause_start(DUMMY_SPAN); for pattern in entry.patterns.iter() { let pat = lower_case_pattern( @@ -418,7 +419,7 @@ fn lower_case_pattern( Ok(child) }, ast::CasePattern::Wildcard => { - let node = b.pat_mut().node_empty(); + let node = b.pat_mut().node_empty(Some(DUMMY_SPAN)); b.pat_mut().wildcard(node); Ok(node) }, @@ -473,20 +474,20 @@ fn lower_value( let v_buf: Result, _> = tup.iter() .map(|v| lower_value(errors, b, scope, v)) .collect(); - Ok(b.prim_tuple(&v_buf?)) + Ok(b.prim_tuple(DUMMY_SPAN, &v_buf?)) } ast::Value::CaptureFunction(m, f, a) => { let m_v = lower_value(errors, b, scope, &*m)?; let f_v = lower_value(errors, b, scope, &*f)?; let a_v = lower_value(errors, b, scope, &*a)?; - Ok(b.prim_capture_function(m_v, f_v, a_v)) + Ok(b.prim_capture_function(DUMMY_SPAN, m_v, f_v, a_v)) } ast::Value::BinOp(lhs, op, rhs) => { let lhs_v = lower_value(errors, b, scope, &*lhs)?; let rhs_v = lower_value(errors, b, scope, &*rhs)?; - Ok(b.prim_binop(*op, lhs_v, rhs_v)) + Ok(b.prim_binop(DUMMY_SPAN, *op, lhs_v, rhs_v)) } ast::Value::List(head, tail) => { let mut acc = tail.as_ref() @@ -495,7 +496,7 @@ fn lower_value( .unwrap_or(b.value(crate::constant::NilTerm)); for v in head.iter().rev() { let new_val = lower_value(errors, b, scope, &*v)?; - acc = b.prim_list_cell(new_val, acc); + acc = b.prim_list_cell(DUMMY_SPAN, new_val, acc); } Ok(acc) } diff --git a/libeir_passes/Cargo.toml b/libeir_passes/Cargo.toml index f583bd7..8f70c37 100644 --- a/libeir_passes/Cargo.toml +++ b/libeir_passes/Cargo.toml @@ -10,6 +10,7 @@ libeir_ir = { path = "../libeir_ir" } libeir_intern = { path = "../libeir_intern" } libeir_util_pattern_compiler = { path = "../util/libeir_util_pattern_compiler" } libeir_util_dot_graph = { path = "../util/libeir_util_dot_graph" } +libeir_diagnostics = { path = "../libeir_diagnostics" } matches = "0.1.8" diff --git a/libeir_passes/src/compile_pattern/lower_cfg.rs b/libeir_passes/src/compile_pattern/lower_cfg.rs index a1dcbb3..ce9ef7d 100644 --- a/libeir_passes/src/compile_pattern/lower_cfg.rs +++ b/libeir_passes/src/compile_pattern/lower_cfg.rs @@ -6,6 +6,8 @@ use libeir_ir::pattern::{ PatternClause, PatternNode }; use libeir_util_pattern_compiler::{ PatternCfg, CfgNodeKind, EdgeRef, NodeIndex }; +use libeir_diagnostics::DUMMY_SPAN; + use super::BFnvHashMap; use super::erlang_pattern_provider::{ ErlangPatternProvider, NodeKind, Var, ValueOrConst, @@ -152,14 +154,21 @@ fn lower_cfg_rec( block: Block, node: NodeIndex, ) { + let block_value = b.fun().block_value(block); + let block_span = b.fun().value_locations(block_value) + .map(|spans| spans.first().copied().unwrap_or(DUMMY_SPAN)) + .unwrap_or(DUMMY_SPAN); match cfg.graph[node] { CfgNodeKind::Root => unreachable!(), CfgNodeKind::Match(var) => { let match_val = ctx.get_var_value(var); + let span = b.fun().value_locations(match_val) + .map(|spans| spans.first().copied().unwrap_or(DUMMY_SPAN)) + .unwrap_or(DUMMY_SPAN); let mut wildcard_node = None; - let mut match_builder = b.op_match_build(); + let mut match_builder = b.op_match_build(span); for outgoing in cfg.graph.edges(node) { let weight = outgoing.weight(); @@ -271,7 +280,7 @@ fn lower_cfg_rec( let wildcard_edge = wildcard_node.unwrap(); assert!(wildcard_edge.weight().variable_binds.len() == 0); - let wildcard_block = match_builder.push_wildcard(b); + let wildcard_block = match_builder.push_wildcard(span, b); lower_cfg_rec( bump, b, ctx, cfg, clauses, wildcard_block, wildcard_edge.target(), @@ -295,16 +304,16 @@ fn lower_cfg_rec( // Call to guard lambda let (ok_block, thr_block) = b.op_call_function( - block, ctx.destinations.guards[leaf_num], &args); + block_span, block, ctx.destinations.guards[leaf_num], &args); let ok_ret = b.block_args(ok_block)[0]; // Throw is unreachable - b.op_unreachable(thr_block); + b.op_unreachable(block_span, thr_block); // Conditional on return let (true_block, false_block, non_block) = b.op_if_bool( - ok_block, ok_ret); - b.op_unreachable(non_block); + block_span, ok_block, ok_ret); + b.op_unreachable(block_span, non_block); // If guard succeeds, we enter the body b.op_call_flow(true_block, ctx.destinations.bodies[leaf_num], &args); diff --git a/libeir_syntax_erl/src/lower/exception_handler_stack.rs b/libeir_syntax_erl/src/lower/exception_handler_stack.rs index 937ad18..514c917 100644 --- a/libeir_syntax_erl/src/lower/exception_handler_stack.rs +++ b/libeir_syntax_erl/src/lower/exception_handler_stack.rs @@ -1,4 +1,5 @@ use libeir_ir::{ FunctionBuilder, Value, Block }; +use libeir_diagnostics::ByteSpan; pub struct ExceptionHandlerStack { stack: Vec<(Value, bool)>, @@ -32,10 +33,10 @@ impl ExceptionHandlerStack { } pub fn make_error_jump( - &self, b: &mut FunctionBuilder, block: Block, + &self, b: &mut FunctionBuilder, span: ByteSpan, block: Block, typ: Value, error: Value) { - let cont = b.op_trace_capture_raw(block); + let cont = b.op_trace_capture_raw(span, block); let trace = b.block_args(cont)[0]; self.make_error_jump_trace(b, cont, typ, error, trace); } diff --git a/libeir_syntax_erl/src/lower/expr/binary.rs b/libeir_syntax_erl/src/lower/expr/binary.rs index 0d05e24..c49fee0 100644 --- a/libeir_syntax_erl/src/lower/expr/binary.rs +++ b/libeir_syntax_erl/src/lower/expr/binary.rs @@ -311,11 +311,11 @@ pub(crate) fn lower_binary_elem( }; let err_cont = map_block!(block, b.op_binary_push( - block, spec, bin, bit_val, size_val)); + elem.span, block, spec, bin, bit_val, size_val)); let res_arg = b.block_args(block)[0]; // TODO: Proper error - b.op_unreachable(err_cont); + b.op_unreachable(elem.span, err_cont); (block, res_arg) } diff --git a/libeir_syntax_erl/src/lower/expr/binary_expr.rs b/libeir_syntax_erl/src/lower/expr/binary_expr.rs index a406ca2..ce615e7 100644 --- a/libeir_syntax_erl/src/lower/expr/binary_expr.rs +++ b/libeir_syntax_erl/src/lower/expr/binary_expr.rs @@ -17,6 +17,7 @@ pub(super) fn lower_binary_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut expr: &BinaryExpr) -> (IrBlock, IrValue) { let BinaryExpr { lhs, rhs, op, id: _, span } = expr; + let span = *span; match op { BinaryOp::AndAlso => { @@ -25,7 +26,7 @@ pub(super) fn lower_binary_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut let ret_block = b.block_insert(); let ret_val = b.block_arg_insert(ret_block); - let (true1_block, false1_block, non1_block) = b.op_if_bool(l1_block, lhs_val); + let (true1_block, false1_block, non1_block) = b.op_if_bool(span, l1_block, lhs_val); // True branch let (l2_block, rhs_val) = lower_single(ctx, b, true1_block, rhs); @@ -38,8 +39,8 @@ pub(super) fn lower_binary_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut // Nonbool branch let typ_val = b.value(Symbol::intern("error")); let err_atom = b.value(Symbol::intern("badarg")); - let err_val = b.prim_tuple(&[err_atom, lhs_val]); - ctx.exc_stack.make_error_jump(b, non1_block, typ_val, err_val); + let err_val = b.prim_tuple(span, &[err_atom, lhs_val]); + ctx.exc_stack.make_error_jump(b, span, non1_block, typ_val, err_val); (ret_block, ret_val) } @@ -49,7 +50,7 @@ pub(super) fn lower_binary_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut let ret_block = b.block_insert(); let ret_val = b.block_arg_insert(ret_block); - let (true1_block, false1_block, non1_block) = b.op_if_bool(l1_block, lhs_val); + let (true1_block, false1_block, non1_block) = b.op_if_bool(span, l1_block, lhs_val); // True branch let true_val = b.value(true); @@ -65,9 +66,9 @@ pub(super) fn lower_binary_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut let typ_val = b.value(Symbol::intern("error")); let err_atom = b.value(Symbol::intern("badarg")); - let err_val = b.prim_tuple(&[err_atom, lhs_val]); + let err_val = b.prim_tuple(span, &[err_atom, lhs_val]); - ctx.exc_stack.make_error_jump(b, block, typ_val, err_val); + ctx.exc_stack.make_error_jump(b, span, block, typ_val, err_val); } (ret_block, ret_val) @@ -105,7 +106,7 @@ pub(super) fn lower_binary_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut _ => unimplemented!("{:?}", op), }; - ctx.call_function(b, block, *span, m, f, &[lhs_val, rhs_val]) + ctx.call_function(b, block, span, m, f, &[lhs_val, rhs_val]) } } diff --git a/libeir_syntax_erl/src/lower/expr/case.rs b/libeir_syntax_erl/src/lower/expr/case.rs index ebcc891..28e848b 100644 --- a/libeir_syntax_erl/src/lower/expr/case.rs +++ b/libeir_syntax_erl/src/lower/expr/case.rs @@ -21,17 +21,18 @@ pub(super) fn lower_case_expr( case: &Case, ) -> (IrBlock, IrValue) { + let span = case.span; let match_val = map_block!(block, lower_single(ctx, b, block, &case.expr)); let no_match = b.block_insert(); { let typ_val = b.value(Symbol::intern("error")); let case_clause_val = b.value(Symbol::intern("case_clause")); - let err_val = b.prim_tuple(&[case_clause_val, match_val]); - ctx.exc_stack.make_error_jump(b, no_match, typ_val, err_val); + let err_val = b.prim_tuple(span, &[case_clause_val, match_val]); + ctx.exc_stack.make_error_jump(b, span, no_match, typ_val, err_val); } - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(span); case_b.match_on = Some(match_val); case_b.no_match = Some(b.value(no_match)); @@ -40,7 +41,7 @@ pub(super) fn lower_case_expr( let mut scope_merge = ScopeMerge::new(); for clause in case.clauses.iter() { - match lower_clause(ctx, b, &mut block, false, [&clause.pattern].iter() + match lower_clause(ctx, b, &mut block, false, clause.span, [&clause.pattern].iter() .map(|i| *i), clause.guard.as_ref()) { Ok(lowered) => { @@ -79,6 +80,7 @@ pub(super) fn lower_case_expr( pub(super) fn lower_if_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, if_expr: &If) -> (IrBlock, IrValue) { + let span = if_expr.span; let match_val = b.prim_value_list(&[]); let no_match = b.block_insert(); @@ -86,11 +88,11 @@ pub(super) fn lower_if_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut blo let block = no_match; let typ_val = b.value(Symbol::intern("error")); let badmatch_val = b.value(Symbol::intern("badmatch")); - let err_val = b.prim_tuple(&[badmatch_val, match_val]); - ctx.exc_stack.make_error_jump(b, block, typ_val, err_val); + let err_val = b.prim_tuple(span, &[badmatch_val, match_val]); + ctx.exc_stack.make_error_jump(b, span, block, typ_val, err_val); } - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(span); case_b.match_on = Some(match_val); case_b.no_match = Some(b.value(no_match)); @@ -100,7 +102,7 @@ pub(super) fn lower_if_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut blo for clause in if_expr.clauses.iter() { match lower_clause(ctx, b, &mut block, false, - [].iter(), Some(&clause.guards)) + span, [].iter(), Some(&clause.guards)) { Ok(lowered) => { let (scope_token, body) = lowered.make_body(ctx, b); diff --git a/libeir_syntax_erl/src/lower/expr/catch.rs b/libeir_syntax_erl/src/lower/expr/catch.rs index 886ad8c..2a60fa9 100644 --- a/libeir_syntax_erl/src/lower/expr/catch.rs +++ b/libeir_syntax_erl/src/lower/expr/catch.rs @@ -19,6 +19,7 @@ use crate::lower::pattern::lower_clause; pub(super) fn lower_try_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, try_expr: &Try) -> (IrBlock, IrValue) { + let span = try_expr.span; let exc_block = b.block_insert(); let exc_type = b.block_arg_insert(exc_block); @@ -42,17 +43,17 @@ pub(super) fn lower_try_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut bl let block = no_match; let typ_val = b.value(Symbol::intern("error")); let try_clause_val = b.value(Symbol::intern("try_clause")); - let err_val = b.prim_tuple(&[try_clause_val, body_ret]); - ctx.exc_stack.make_error_jump(b, block, typ_val, err_val); + let err_val = b.prim_tuple(span, &[try_clause_val, body_ret]); + ctx.exc_stack.make_error_jump(b, span, block, typ_val, err_val); } - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(span); case_b.match_on = Some(body_ret); case_b.no_match = Some(b.value(no_match)); for clause in clauses { match lower_clause(ctx, b, &mut block, false, - [&clause.pattern].iter().map(|i| *i), + clause.span, [&clause.pattern].iter().map(|i| *i), clause.guard.as_ref()) { Ok(lowered) => { @@ -92,7 +93,7 @@ pub(super) fn lower_try_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut bl let match_val = b.prim_value_list(&[exc_type, exc_error]); - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(span); case_b.match_on = Some(match_val); case_b.no_match = Some(b.value(catch_no_match_block)); @@ -108,7 +109,7 @@ pub(super) fn lower_try_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut bl }; match lower_clause( - ctx, b, &mut block, false, + ctx, b, &mut block, false, clause.span, [ &kind_expr, &clause.error, @@ -183,25 +184,26 @@ pub(super) fn lower_try_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut bl pub(super) fn lower_catch_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, catch_expr: &Catch) -> (IrBlock, IrValue) { + let span = catch_expr.span; let exc_block = b.block_insert(); let exc_type = b.block_arg_insert(exc_block); let exc_error = b.block_arg_insert(exc_block); let exc_trace = b.block_arg_insert(exc_block); - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(span); // Atoms let big_exit_atom = b.value(Symbol::intern("EXIT")); let make_value_clause = |b: &mut FunctionBuilder, case_b: &mut CaseBuilder, val: IrValue| { - let clause = b.pat_mut().clause_start(); + let clause = b.pat_mut().clause_start(span); // Value case_b.push_value(val, b); let pat_val = b.pat_mut().clause_value(clause); - let pat = b.pat_mut().node_empty(); + let pat = b.pat_mut().node_empty(Some(span)); b.pat_mut().value(pat, pat_val); b.pat_mut().clause_node_push(clause, pat); @@ -230,7 +232,7 @@ pub(super) fn lower_catch_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut // no_match is unreachable let no_match = b.block_insert(); - b.op_unreachable(no_match); + b.op_unreachable(span, no_match); // Guard lambda returning true let guard = b.block_insert(); @@ -250,8 +252,8 @@ pub(super) fn lower_catch_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut let error_block_val = b.value(error_block); case_b.push_clause(error_clause, guard_val, error_block_val, b); - let inner_tup = b.prim_tuple(&[exc_error, exc_trace]); - let ret_tup = b.prim_tuple(&[big_exit_atom, inner_tup]); + let inner_tup = b.prim_tuple(span, &[exc_error, exc_trace]); + let ret_tup = b.prim_tuple(span, &[big_exit_atom, inner_tup]); b.op_call_flow(error_block, join_block, &[ret_tup]); } @@ -262,7 +264,7 @@ pub(super) fn lower_catch_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut let exit_block_val = b.value(exit_block); case_b.push_clause(exit_clause, guard_val, exit_block_val, b); - let ret_tup = b.prim_tuple(&[big_exit_atom, exc_error]); + let ret_tup = b.prim_tuple(span, &[big_exit_atom, exc_error]); b.op_call_flow(exit_block, join_block, &[ret_tup]); } diff --git a/libeir_syntax_erl/src/lower/expr/comprehension.rs b/libeir_syntax_erl/src/lower/expr/comprehension.rs index ea5ee4a..0d26a9e 100644 --- a/libeir_syntax_erl/src/lower/expr/comprehension.rs +++ b/libeir_syntax_erl/src/lower/expr/comprehension.rs @@ -24,6 +24,7 @@ where F: Fn(&mut LowerCtx, &mut FunctionBuilder, IrBlock, IrValue) -> (IrBlock, } else { match &quals[0] { Expr::Generator(gen) => { + let gen_span = gen.span; // loop_block(list_val, acc) // loop_block(loop_list_arg, loop_acc_arg): @@ -54,15 +55,15 @@ where F: Fn(&mut LowerCtx, &mut FunctionBuilder, IrBlock, IrValue) -> (IrBlock, // Check for nil and unpack list let nil = b.value(NilTerm); - let comp_res = b.prim_binop(BinOp::Equal, loop_list_arg, nil); - let (is_nil_block, non_nil_block) = b.op_if_bool_strict(loop_block, comp_res); + let comp_res = b.prim_binop(gen_span, BinOp::Equal, loop_list_arg, nil); + let (is_nil_block, non_nil_block) = b.op_if_bool_strict(gen_span, loop_block, comp_res); ret_block = is_nil_block; ret_val = loop_acc_arg; - let mut match_builder = b.op_match_build(); + let mut match_builder = b.op_match_build(gen_span); let unpack_ok_block = match_builder.push_list_cell(b); - let unpack_fail_block = match_builder.push_wildcard(b); + let unpack_fail_block = match_builder.push_wildcard(gen_span, b); match_builder.finish(non_nil_block, loop_list_arg, b); let head_val = b.block_args(unpack_ok_block)[0]; @@ -72,7 +73,7 @@ where F: Fn(&mut LowerCtx, &mut FunctionBuilder, IrBlock, IrValue) -> (IrBlock, let typ = b.value(Symbol::intern("error")); let error = b.value(Symbol::intern("function_clause")); ctx.exc_stack.make_error_jump( - b, unpack_fail_block, typ, error); + b, gen_span, unpack_fail_block, typ, error); } // When there is no match, continue iterating @@ -80,14 +81,15 @@ where F: Fn(&mut LowerCtx, &mut FunctionBuilder, IrBlock, IrValue) -> (IrBlock, b.op_call_flow(no_match, loop_block, &[tail_val, acc]); block = unpack_ok_block; + let pattern_span = gen.pattern.span(); match lower_clause(ctx, b, &mut block, false, - [&*gen.pattern].iter().map(|i| *i), None) + pattern_span, [&*gen.pattern].iter().map(|i| *i), None) { Ok(lowered) => { let (scope_token, body) = lowered.make_body(ctx, b); - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(pattern_span); case_b.match_on = Some(head_val); case_b.no_match = Some(b.value(no_match)); @@ -117,7 +119,8 @@ where F: Fn(&mut LowerCtx, &mut FunctionBuilder, IrBlock, IrValue) -> (IrBlock, expr => { let bool_val = map_block!(block, lower_single_same_scope( ctx, b, block, expr)); - let (true_block, false_block, else_block) = b.op_if_bool(block, bool_val); + let span = expr.span(); + let (true_block, false_block, else_block) = b.op_if_bool(span, block, bool_val); let join_block = b.block_insert(); let join_arg = b.block_arg_insert(join_block); @@ -137,8 +140,9 @@ where F: Fn(&mut LowerCtx, &mut FunctionBuilder, IrBlock, IrValue) -> (IrBlock, pub(super) fn lower_list_comprehension_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, compr: &ListComprehension) -> (IrBlock, IrValue) { let inner = |ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, acc: IrValue| { + let span = compr.body.span(); let val = map_block!(block, lower_single(ctx, b, block, &compr.body)); - let cell = b.prim_list_cell(val, acc); + let cell = b.prim_list_cell(span, val, acc); (block, cell) }; diff --git a/libeir_syntax_erl/src/lower/expr/map.rs b/libeir_syntax_erl/src/lower/expr/map.rs index 29318e5..c34176a 100644 --- a/libeir_syntax_erl/src/lower/expr/map.rs +++ b/libeir_syntax_erl/src/lower/expr/map.rs @@ -18,7 +18,7 @@ pub(super) fn lower_map_update_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, { let entry_map_val = map_block!(block, lower_single( ctx, b, block, &map.map)); - let mut map_builder = b.op_map_put_build(entry_map_val); + let mut map_builder = b.op_map_put_build(map.span, entry_map_val); for field in map.updates.iter() { let (key, value, action) = match field { @@ -42,8 +42,8 @@ pub(super) fn lower_map_update_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, let typ_val = b.value(Symbol::intern("error")); let badmatch_val = b.value(Symbol::intern("badkey")); let failed_key = b.block_args(fail)[0]; - let err_val = b.prim_tuple(&[badmatch_val, failed_key]); - ctx.exc_stack.make_error_jump(b, fail, typ_val, err_val); + let err_val = b.prim_tuple(map.span, &[badmatch_val, failed_key]); + ctx.exc_stack.make_error_jump(b, map.span, fail, typ_val, err_val); (ok, b.block_args(ok)[0]) } @@ -53,7 +53,7 @@ pub(super) fn lower_map_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, map: &Map) -> (IrBlock, IrValue) { let empty_map = b.value(EmptyMap); - let mut map_builder = b.op_map_put_build(empty_map); + let mut map_builder = b.op_map_put_build(map.span, empty_map); for field in map.fields.iter() { let (key, value, action) = match field { @@ -77,8 +77,8 @@ pub(super) fn lower_map_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, let typ_val = b.value(Symbol::intern("error")); let badmatch_val = b.value(Symbol::intern("badkey")); let failed_key = b.block_args(fail)[0]; - let err_val = b.prim_tuple(&[badmatch_val, failed_key]); - ctx.exc_stack.make_error_jump(b, fail, typ_val, err_val); + let err_val = b.prim_tuple(map.span, &[badmatch_val, failed_key]); + ctx.exc_stack.make_error_jump(b, map.span, fail, typ_val, err_val); (ok, b.block_args(ok)[0]) } diff --git a/libeir_syntax_erl/src/lower/expr/mod.rs b/libeir_syntax_erl/src/lower/expr/mod.rs index 2e23dd3..283ff22 100644 --- a/libeir_syntax_erl/src/lower/expr/mod.rs +++ b/libeir_syntax_erl/src/lower/expr/mod.rs @@ -97,6 +97,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, let mut block = block; match expr { Expr::Apply(Apply { span, callee, args, .. }) => { + let span = *span; let mut arg_vals = vec![]; let arity_val = b.value(args.len()); @@ -106,7 +107,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, let mod_val = map_block!(block, lower_single(ctx, b, block, module)); let fun_val = map_block!(block, lower_single(ctx, b, block, function)); - b.prim_capture_function(mod_val, fun_val, arity_val) + b.prim_capture_function(span, mod_val, fun_val, arity_val) } Expr::Literal(Literal::Atom(_id, name)) => { let local = LocalFunctionName { @@ -132,7 +133,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, let mod_val = b.value(module); let fun_val = b.value(function); - b.prim_capture_function(mod_val, fun_val, arity_val) + b.prim_capture_function(span, mod_val, fun_val, arity_val) } expr => { map_block!(block, lower_single_same_scope(ctx, b, block, expr)) @@ -146,11 +147,11 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, arg_vals.push(arg_val); } - let loc = ctx.current_location(b, *span); + let loc = ctx.current_location(b, span); b.block_set_location(block, loc); let (ok_block, fail_block) = b.op_call_function( - block, callee_val, &arg_vals); + span, block, callee_val, &arg_vals); let fail_type = b.block_args(fail_block)[0]; let fail_error = b.block_args(fail_block)[1]; @@ -187,17 +188,17 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, { let typ_val = b.value(Symbol::intern("error")); let badmatch_val = b.value(Symbol::intern("badmatch")); - let err_val = b.prim_tuple(&[badmatch_val, match_val]); - ctx.exc_stack.make_error_jump(b, no_match, typ_val, err_val); + let err_val = b.prim_tuple(mat.span, &[badmatch_val, match_val]); + ctx.exc_stack.make_error_jump(b, mat.span, no_match, typ_val, err_val); } - match lower_clause(ctx, b, &mut block, false, + match lower_clause(ctx, b, &mut block, false, mat.span, [&mat.pattern].iter().map(|i| &***i), None) { Ok(lowered) => { let (_scope_token, body) = lowered.make_body(ctx, b); - let mut match_case = b.op_case_build(); + let mut match_case = b.op_case_build(mat.span); match_case.match_on = Some(match_val); match_case.no_match = Some(b.value(no_match)); @@ -227,7 +228,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, Expr::Cons(cons) => { let head = map_block!(block, lower_single(ctx, b, block, &cons.head)); let tail = map_block!(block, lower_single(ctx, b, block, &cons.tail)); - let list = b.prim_list_cell(head, tail); + let list = b.prim_list_cell(cons.span, head, tail); (block, list) } Expr::Nil(_nil) => { @@ -243,7 +244,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, vals.push(val); } - let tuple = b.prim_tuple(&vals); + let tuple = b.prim_tuple(tup.span, &vals); (block, tuple) } Expr::Fun(fun) => { @@ -271,19 +272,19 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, map_block!(block, lower_single(ctx, b, block, &after.timeout)) } else { - b.op_unreachable(after_block); + b.op_unreachable(recv.span, after_block); b.value(Ident::from_str("infinity")) }; // Receive start - let mut recv_start_b = b.op_intrinsic_build(Symbol::intern("receive_start")); + let mut recv_start_b = b.op_intrinsic_build(recv.span, Symbol::intern("receive_start")); recv_start_b.push_value(recv_wait_block, b); recv_start_b.push_value(after_timeout_val, b); recv_start_b.block = Some(block); recv_start_b.finish(b); // Receive wait - let mut recv_start_b = b.op_intrinsic_build(Symbol::intern("receive_wait")); + let mut recv_start_b = b.op_intrinsic_build(recv.span, Symbol::intern("receive_wait")); recv_start_b.push_value(after_block, b); recv_start_b.push_value(body_block, b); recv_start_b.block = Some(recv_wait_block); @@ -294,7 +295,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, let no_match = b.block_insert(); b.op_call_flow(no_match, recv_wait_block, &[recv_ref_val]); - let mut case_b = b.op_case_build(); + let mut case_b = b.op_case_build(recv.span); case_b.match_on = Some(body_message_arg); case_b.no_match = Some(b.value(no_match)); @@ -302,7 +303,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, for clause in clauses.iter() { match lower_clause(ctx, b, &mut body_block, false, - [&clause.pattern].iter().map(|i| *i), + clause.span, [&clause.pattern].iter().map(|i| *i), clause.guard.as_ref()) { Ok(lowered) => { @@ -315,7 +316,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, // This enables us to do things like copy from // a heap fragment to the main process heap. let mut recv_done_b = b.op_intrinsic_build( - Symbol::intern("receive_done")); + recv.span, Symbol::intern("receive_done")); recv_done_b.push_value(body_mapped, b); for bind in lowered.binds.iter() { let val = b.block_arg_insert(body); @@ -365,7 +366,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, let function = b.value(resolved.function); let arity = b.value(resolved.arity); - let fun_val = b.prim_capture_function(module, function, arity); + let fun_val = b.prim_capture_function(resolved.span, module, function, arity); (block, fun_val) } @@ -381,7 +382,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, let function = b.value(resolved.function); let arity = b.value(resolved.arity); - let fun_val = b.prim_capture_function(module, function, arity); + let fun_val = b.prim_capture_function(partial.span, module, function, arity); (block, fun_val) } @@ -400,7 +401,7 @@ fn lower_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: IrBlock, Arity::Var(var) => b.value(var), }; - let fun_val = b.prim_capture_function(module, function, arity); + let fun_val = b.prim_capture_function(unresolved.span, module, function, arity); (block, fun_val) } diff --git a/libeir_syntax_erl/src/lower/expr/record.rs b/libeir_syntax_erl/src/lower/expr/record.rs index 8446c98..4effe29 100644 --- a/libeir_syntax_erl/src/lower/expr/record.rs +++ b/libeir_syntax_erl/src/lower/expr/record.rs @@ -4,6 +4,7 @@ use libeir_ir::{ Block as IrBlock, BinOp as IrBinOp, }; +use libeir_diagnostics::ByteSpan; use libeir_intern::Symbol; @@ -12,34 +13,35 @@ use crate::parser::ast::{ Record, RecordAccess, RecordUpdate, RecordIndex }; use crate::lower::{ LowerCtx, LowerError }; use crate::lower::expr::lower_single; -fn make_rec_fail(ctx: &mut LowerCtx, b: &mut FunctionBuilder, recname_val: IrValue) -> IrBlock { +fn make_rec_fail(ctx: &mut LowerCtx, b: &mut FunctionBuilder, span: ByteSpan, recname_val: IrValue) -> IrBlock { let fail_block = b.block_insert(); let block = fail_block; let fail_type = b.value(Symbol::intern("error")); // TODO double check correct type let badrecord_val = b.value(Symbol::intern("badrecord")); - let fail_error = b.prim_tuple(&[badrecord_val, recname_val]); + let fail_error = b.prim_tuple(span, &[badrecord_val, recname_val]); - ctx.exc_stack.make_error_jump(b, block, fail_type, fail_error); + ctx.exc_stack.make_error_jump(b, span, block, fail_type, fail_error); fail_block } pub(super) fn lower_record_access_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, rec: &RecordAccess) -> (IrBlock, IrValue) { + let span = rec.span; let rec_def = &ctx.module.records[&rec.name.name]; let recname_val = b.value(rec.name); let idx = rec_def.field_idx_map[&rec.field]; - let fail_block = make_rec_fail(ctx, b, recname_val); + let fail_block = make_rec_fail(ctx, b, span, recname_val); let record_val = map_block!(block, lower_single(ctx, b, block, &rec.record)); - let mut match_builder = b.op_match_build(); + let mut match_builder = b.op_match_build(span); let unpack_ok_block = match_builder.push_tuple(rec_def.record.fields.len() + 1, b); - let unpack_fail_block = match_builder.push_wildcard(b); + let unpack_fail_block = match_builder.push_wildcard(span, b); match_builder.finish(block, record_val, b); block = unpack_ok_block; @@ -48,8 +50,8 @@ pub(super) fn lower_record_access_expr(ctx: &mut LowerCtx, b: &mut FunctionBuild let recname_test_val = b.block_args(block)[0]; let rec_field_val = b.block_args(block)[idx + 1]; - let eq_cond = b.prim_binop(IrBinOp::Equal, recname_test_val, recname_val); - let eq_fail_block = map_block!(block, b.op_if_bool_strict(block, eq_cond)); + let eq_cond = b.prim_binop(span, IrBinOp::Equal, recname_test_val, recname_val); + let eq_fail_block = map_block!(block, b.op_if_bool_strict(span, block, eq_cond)); b.op_call_flow(eq_fail_block, fail_block, &[]); (block, rec_field_val) @@ -57,20 +59,21 @@ pub(super) fn lower_record_access_expr(ctx: &mut LowerCtx, b: &mut FunctionBuild pub(super) fn lower_record_update_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, rec: &RecordUpdate) -> (IrBlock, IrValue) { + let span = rec.span; // TODO Warn/error when updates overlap? let rec_def = &ctx.module.records[&rec.name.name]; let recname_val = b.value(rec.name); let num_fields = rec_def.record.fields.len(); - let fail_block = make_rec_fail(ctx, b, recname_val); + let fail_block = make_rec_fail(ctx, b, span, recname_val); // Unpack tuple let record_val = map_block!(block, lower_single(ctx, b, block, &rec.record)); - let mut match_builder = b.op_match_build(); + let mut match_builder = b.op_match_build(span); let unpack_ok_block = match_builder.push_tuple(num_fields + 1, b); - let unpack_fail_block = match_builder.push_wildcard(b); + let unpack_fail_block = match_builder.push_wildcard(span, b); match_builder.finish(block, record_val, b); block = unpack_ok_block; @@ -84,8 +87,8 @@ pub(super) fn lower_record_update_expr(ctx: &mut LowerCtx, b: &mut FunctionBuild // Check first tuple element let recname_test_val = b.block_args(block)[0]; - let eq_cond = b.prim_binop(IrBinOp::Equal, recname_test_val, recname_val); - let eq_fail_block = map_block!(block, b.op_if_bool_strict(block, eq_cond)); + let eq_cond = b.prim_binop(span, IrBinOp::Equal, recname_test_val, recname_val); + let eq_fail_block = map_block!(block, b.op_if_bool_strict(span, block, eq_cond)); b.op_call_flow(eq_fail_block, fail_block, &[]); // Update fields @@ -100,13 +103,14 @@ pub(super) fn lower_record_update_expr(ctx: &mut LowerCtx, b: &mut FunctionBuild let mut tup_values = Vec::new(); tup_values.push(recname_val); tup_values.extend(elems.iter().cloned()); - let tup = b.prim_tuple(&tup_values); + let tup = b.prim_tuple(span, &tup_values); (block, tup) } pub(super) fn lower_record_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut block: IrBlock, rec: &Record) -> (IrBlock, IrValue) { + let span = rec.span; let rec_def = &ctx.module.records[&rec.name.name]; let recname_val = b.value(rec.name); @@ -149,7 +153,7 @@ pub(super) fn lower_record_expr(ctx: &mut LowerCtx, b: &mut FunctionBuilder, mut let mut tup_values = Vec::new(); tup_values.push(recname_val); tup_values.extend(elems.iter().map(|e| e.unwrap())); - let tup = b.prim_tuple(&tup_values); + let tup = b.prim_tuple(span, &tup_values); (block, tup) } diff --git a/libeir_syntax_erl/src/lower/mod.rs b/libeir_syntax_erl/src/lower/mod.rs index 6773b45..de98219 100644 --- a/libeir_syntax_erl/src/lower/mod.rs +++ b/libeir_syntax_erl/src/lower/mod.rs @@ -133,7 +133,7 @@ impl<'a> LowerCtx<'a> { where M: IntoValue, F: IntoValue { - let fun_val = b.prim_capture_function(m, f, args.len()); + let fun_val = b.prim_capture_function(span, m, f, args.len()); self.val_buf.clear(); for arg in args.iter() { @@ -144,7 +144,7 @@ impl<'a> LowerCtx<'a> { b.block_set_location(block, loc); let (ok_block, fail_block) = b.op_call_function( - block, fun_val, args); + span, block, fun_val, args); let fail_type = b.block_args(fail_block)[0]; let fail_error = b.block_args(fail_block)[1]; @@ -190,7 +190,7 @@ pub fn lower_module<'a>( assert!(ctx.scope.height() == 0); ctx.fun_num = 0; - let fun_def = ir_module.add_function(ident.function, function.arity); + let fun_def = ir_module.add_function(function.span, ident.function, function.arity); let mut fun = fun_def.function_mut(); let mut builder = FunctionBuilder::new(&mut fun); @@ -220,7 +220,7 @@ fn lower_function( ctx: &mut LowerCtx, b: &mut FunctionBuilder, fun: &Function, ) -> IrBlock { - let entry = b.block_insert(); + let entry = b.block_insert_with_span(Some(fun.span())); match fun { Function::Named(_named) => { @@ -245,7 +245,7 @@ fn lower_function_base( ctx: &mut LowerCtx, b: &mut FunctionBuilder, // The block the function should be lowered into entry: IrBlock, - _span: ByteSpan, + span: ByteSpan, arity: usize, clauses: &[FunctionClause], ) { @@ -275,14 +275,15 @@ fn lower_function_base( { let typ_val = b.value(Symbol::intern("error")); let err_val = b.value(Symbol::intern("function_clause")); - ctx.exc_stack.make_error_jump(b, match_fail_block, typ_val, err_val); + ctx.exc_stack.make_error_jump(b, span, match_fail_block, typ_val, err_val); } let entry_exc_height = ctx.exc_stack.len(); // Top level function case expression { - let mut func_case = b.op_case_build(); + // TODO: Fuse locations of function heads + let mut func_case = b.op_case_build(span); func_case.match_on = Some(args_list); func_case.no_match = Some(b.value(match_fail_block)); @@ -290,6 +291,7 @@ fn lower_function_base( for clause in clauses.iter() { match lower_clause(ctx, b, &mut block, true, + clause.span, clause.params.iter(), clause.guard.as_ref()) { diff --git a/libeir_syntax_erl/src/lower/pattern/mod.rs b/libeir_syntax_erl/src/lower/pattern/mod.rs index 0afdab5..cc7bf6f 100644 --- a/libeir_syntax_erl/src/lower/pattern/mod.rs +++ b/libeir_syntax_erl/src/lower/pattern/mod.rs @@ -10,7 +10,7 @@ use libeir_ir::pattern::{ PatternClause, PatternValue, }; - +use libeir_diagnostics::ByteSpan; use crate::parser::ast::{ Expr, Guard }; @@ -32,6 +32,7 @@ enum EqGuard { } struct ClauseLowerCtx { + span: ByteSpan, // The clause we are constructing pat_clause: PatternClause, @@ -126,16 +127,18 @@ impl UnreachableClause { /// * The body is empty pub(super) fn lower_clause<'a, P>( ctx: &mut LowerCtx, b: &mut FunctionBuilder, pre_case: &mut IrBlock, - shadow: bool, patterns: P, guard: Option<&Vec>, + shadow: bool, span: ByteSpan, patterns: P, guard: Option<&Vec>, ) -> Result where P: Iterator, { assert!(b.fun().block_kind(*pre_case).is_none()); - let pat_clause = b.pat_mut().clause_start(); + let pat_clause = b.pat_mut().clause_start(span); let mut clause_ctx = ClauseLowerCtx { + span, + pat_clause, pre_case: *pre_case, @@ -245,14 +248,14 @@ impl ClauseLowerCtx { }; let fun_val = b.prim_capture_function( - erlang_atom, exact_eq_atom, two_atom); + self.span, erlang_atom, exact_eq_atom, two_atom); let (ok_block, err_block) = b.op_call_function( - block, fun_val, &[lhs, rhs]); + self.span, block, fun_val, &[lhs, rhs]); let res_val = b.block_args(ok_block)[0]; block = ok_block; - b.op_unreachable(err_block); + b.op_unreachable(self.span, err_block); top_and.push(res_val); } @@ -270,17 +273,17 @@ impl ClauseLowerCtx { block = block_new; } - let val = b.prim_logic_op(LogicOp::And, &and); + let val = b.prim_logic_op(guard.span, LogicOp::And, &and); and.clear(); or.push(val); } - let val = b.prim_logic_op(LogicOp::Or, &or); + let val = b.prim_logic_op(self.span, LogicOp::Or, &or); or.clear(); top_and.push(val); } - let result_bool = b.prim_logic_op(LogicOp::And, &top_and); + let result_bool = b.prim_logic_op(self.span, LogicOp::And, &top_and); b.op_call_flow(block, ret_cont, &[result_bool]); ctx.exc_stack.pop_handler(); diff --git a/libeir_syntax_erl/src/lower/pattern/tree/lower.rs b/libeir_syntax_erl/src/lower/pattern/tree/lower.rs index c880aa0..d213fad 100644 --- a/libeir_syntax_erl/src/lower/pattern/tree/lower.rs +++ b/libeir_syntax_erl/src/lower/pattern/tree/lower.rs @@ -7,6 +7,7 @@ use libeir_ir::{ PatternNode, }; use libeir_intern::Ident; +use libeir_diagnostics::DUMMY_SPAN; use super::{ Tree, TreeNode, TreeNodeKind, ConstraintKind }; use super::super::{ ClauseLowerCtx, EqGuard }; @@ -84,7 +85,7 @@ fn create_nodes( ) { assert!(!map.contains_key(&node)); - let p_node = b.pat_mut().node_empty(); + let p_node = b.pat_mut().node_empty(Some(DUMMY_SPAN)); map.insert(node, p_node); match &t.nodes[node] { From 9f906a3400bd9b26ec16e89dad84f7a153b66cd3 Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Fri, 10 Apr 2020 23:01:17 -0400 Subject: [PATCH 6/8] Expose additional colors for diagnostics --- libeir_diagnostics/src/reporting/emitter.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libeir_diagnostics/src/reporting/emitter.rs b/libeir_diagnostics/src/reporting/emitter.rs index 9a7bc44..a5b7f6c 100644 --- a/libeir_diagnostics/src/reporting/emitter.rs +++ b/libeir_diagnostics/src/reporting/emitter.rs @@ -346,6 +346,16 @@ pub fn yellow_bold() -> ColorSpec { color_bold(Color::Yellow) } +#[inline] +pub fn red() -> ColorSpec { + color(Color::Red) +} + +#[inline] +pub fn red_bold() -> ColorSpec { + color_bold(Color::Red) +} + #[inline] pub fn white() -> ColorSpec { color_bold(Color::White) From 4923793e6f76580bd2d82b3b9dfe0c4851414591 Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Wed, 15 Apr 2020 02:02:18 -0400 Subject: [PATCH 7/8] Prefill interner with throw/exit atoms --- libeir_intern/src/symbol.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libeir_intern/src/symbol.rs b/libeir_intern/src/symbol.rs index 56e6b49..ed8039e 100644 --- a/libeir_intern/src/symbol.rs +++ b/libeir_intern/src/symbol.rs @@ -330,7 +330,7 @@ macro_rules! declare_atoms {( /// Used *only* for testing that the declared atoms have no gaps /// NOTE: The length must be static, so it must be changed when new /// declared keywords are added to the list - pub(super) static DECLARED: [(Symbol, &'static str); 58] = [$(($konst, $string),)*]; + pub(super) static DECLARED: [(Symbol, &'static str); 60] = [$(($konst, $string),)*]; } impl Interner { @@ -410,6 +410,8 @@ declare_atoms! { (55, Deprecated, "deprecated") (56, ModuleCapital,"MODULE") (57, ModuleStringCapital,"MODULE_STRING") + (58, Throw, "throw") + (59, Exit, "exit") } impl Symbol { From 3052dadfb92ad9b9666c1b1eabb941e7374e9f2a Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Fri, 1 May 2020 18:48:43 -0400 Subject: [PATCH 8/8] Refactor diagnostics This changset reworks diagnostics to use codespan/codespan-reporting for the underlying primitives and reporting functionality, but providing our own implementation of the Files trait so that we handle highly concurrent cases better. The CodeMap type is designed to be allocated in an Arc and then cloned freely to pass around, and its use should be primarily to add files and look them up from a source value. Internally it uses two maps, one that holds the actual sources, and one that tracks paths which have been loaded to avoid loading duplicates into memory. The SourceFile type is pretty similar to the old FileMap type, but one of the key differences now is the use of a SourceId to identify sources uniquely. This is how lookups in the CodeMap are performed. One of the big differences is in how we look up files from a source index or span. The old CodeMap worked by searching all files for the one which contained a given ByteIndex, but we don't operate that way now. In order to accomodate this cleanly, there are some new types to represent spans and indices at a high-level, the SourceSpan and SourceIndex types. The SourceIndex type is a 64-bit value that uses half of the bits for the SourceId, and half of the bits for the actual ByteIndex; SourceSpans decompose two SourceIndex values into a single SourceId and two ByteIndex values. The gist is that both types can identify what SourceId they originate from, which can then be used to look up files. The most pressing need for this is in the Diagnostics API exposed by codespan-reporting, since its Label type requires the SourceId, not just any index or span. The changes described here are what produced the bulk of the diff in this patch. I had originally wanted libeir_* to avoid having a concrete representation like CodeMap/SourceFile at all, and entirely build on traits and types provided by codespan/codespan-reporting, but unfortunately there isn't a clean way to do that right now. I think it makes sense to move that way eventually, but to do so, we'll need to sit down and think about how we're using diagnostics in these crates. --- libeir_diagnostics/Cargo.toml | 4 +- libeir_diagnostics/src/codemap.rs | 156 ++++ libeir_diagnostics/src/diagnostics.rs | 20 - libeir_diagnostics/src/diagnostics/codemap.rs | 180 ----- libeir_diagnostics/src/diagnostics/filemap.rs | 581 --------------- libeir_diagnostics/src/diagnostics/index.rs | 349 --------- libeir_diagnostics/src/diagnostics/span.rs | 288 -------- libeir_diagnostics/src/filename.rs | 106 +++ libeir_diagnostics/src/index.rs | 108 +++ libeir_diagnostics/src/lib.rs | 34 +- libeir_diagnostics/src/reporting.rs | 117 --- .../src/reporting/diagnostic.rs | 112 --- libeir_diagnostics/src/reporting/emitter.rs | 385 ---------- libeir_diagnostics/src/source.rs | 150 ++++ libeir_diagnostics/src/span.rs | 85 +++ libeir_frontend/src/abstr_erlang.rs | 60 +- libeir_frontend/src/eir.rs | 58 +- libeir_frontend/src/erlang.rs | 57 +- libeir_frontend/src/lib.rs | 153 ++-- libeir_intern/src/symbol.rs | 10 +- libeir_ir/src/algo/mangle/mod.rs | 6 +- libeir_ir/src/function/builder/mod.rs | 92 +-- libeir_ir/src/function/builder/op.rs | 209 ++++-- libeir_ir/src/function/builder/primop.rs | 203 +++--- libeir_ir/src/function/location.rs | 108 +-- libeir_ir/src/function/mod.rs | 127 ++-- libeir_ir/src/graph/block_graph.rs | 4 +- libeir_ir/src/graph/live_block_graph.rs | 4 +- libeir_ir/src/module.rs | 31 +- libeir_ir/src/pattern/mod.rs | 196 ++--- libeir_ir/src/text/ast/lower.rs | 253 +++---- libeir_ir/src/text/parser/errors.rs | 75 +- libeir_ir/src/text/parser/grammar.lalrpop | 24 +- libeir_ir/src/text/parser/lexer.rs | 85 ++- libeir_ir/src/text/parser/mod.rs | 315 ++++---- .../src/compile_pattern/lower_cfg.rs | 10 +- libeir_syntax_erl/Cargo.toml | 2 +- libeir_syntax_erl/src/abstr/lower.rs | 569 +++++++-------- libeir_syntax_erl/src/lexer/errors.rs | 42 +- libeir_syntax_erl/src/lexer/lexer.rs | 103 ++- libeir_syntax_erl/src/lexer/token.rs | 46 +- libeir_syntax_erl/src/lib.rs | 8 +- libeir_syntax_erl/src/lower/errors.rs | 149 ++-- .../src/lower/exception_handler_stack.rs | 30 +- libeir_syntax_erl/src/lower/expr/literal.rs | 274 +++---- libeir_syntax_erl/src/lower/expr/mod.rs | 3 +- libeir_syntax_erl/src/lower/expr/record.rs | 71 +- libeir_syntax_erl/src/lower/mod.rs | 117 ++- libeir_syntax_erl/src/lower/pattern/mod.rs | 61 +- .../src/lower/pattern/tree/from_expr.rs | 200 +++-- .../src/lower/pattern/tree/lower.rs | 8 +- .../src/lower/pattern/tree/merge.rs | 14 +- .../src/lower/pattern/tree/mod.rs | 66 +- .../src/lower/pattern/tree/promote_values.rs | 11 +- libeir_syntax_erl/src/lower/tests.rs | 21 +- .../src/parser/ast/attributes.rs | 38 +- libeir_syntax_erl/src/parser/ast/expr.rs | 98 +-- libeir_syntax_erl/src/parser/ast/functions.rs | 179 +++-- libeir_syntax_erl/src/parser/ast/mod.rs | 6 +- libeir_syntax_erl/src/parser/ast/module.rs | 604 +++++++++------- libeir_syntax_erl/src/parser/ast/types.rs | 48 +- libeir_syntax_erl/src/parser/errors.rs | 131 ++-- libeir_syntax_erl/src/parser/grammar.lalrpop | 54 +- libeir_syntax_erl/src/parser/macros.rs | 36 +- libeir_syntax_erl/src/parser/mod.rs | 296 ++++---- libeir_syntax_erl/src/parser/visitor/mod.rs | 682 ------------------ .../src/parser/visitor/pretty_print.rs | 36 - .../src/preprocessor/directive.rs | 102 +-- .../src/preprocessor/directives.rs | 167 ++--- libeir_syntax_erl/src/preprocessor/errors.rs | 296 ++++---- .../src/preprocessor/evaluator.rs | 391 ++++++---- libeir_syntax_erl/src/preprocessor/macros.rs | 44 +- libeir_syntax_erl/src/preprocessor/mod.rs | 6 +- .../src/preprocessor/preprocessor.rs | 233 +++--- .../src/preprocessor/token_reader.rs | 56 +- libeir_syntax_erl/src/preprocessor/types.rs | 33 +- libeir_tests/src/control_flow/get_values.rs | 4 - libeir_tests/src/lib.rs | 64 +- libeir_tests/src/otp/mod.rs | 2 - tools/src/compile.rs | 44 +- util/libeir_util_parse/Cargo.toml | 5 +- util/libeir_util_parse/src/errors.rs | 199 +---- util/libeir_util_parse/src/parser.rs | 67 +- util/libeir_util_parse/src/result.rs | 2 - util/libeir_util_parse/src/scanner.rs | 29 +- util/libeir_util_parse/src/source.rs | 171 ++--- util/libeir_util_parse/src/util.rs | 20 +- util/libeir_util_parse_listing/Cargo.toml | 3 + util/libeir_util_parse_listing/src/ast.rs | 24 +- .../src/grammar.lalrpop | 18 +- util/libeir_util_parse_listing/src/parser.rs | 108 ++- util/libeir_util_parse_listing/src/token.rs | 74 +- 92 files changed, 4498 insertions(+), 6422 deletions(-) create mode 100644 libeir_diagnostics/src/codemap.rs delete mode 100644 libeir_diagnostics/src/diagnostics.rs delete mode 100644 libeir_diagnostics/src/diagnostics/codemap.rs delete mode 100644 libeir_diagnostics/src/diagnostics/filemap.rs delete mode 100644 libeir_diagnostics/src/diagnostics/index.rs delete mode 100644 libeir_diagnostics/src/diagnostics/span.rs create mode 100644 libeir_diagnostics/src/filename.rs create mode 100644 libeir_diagnostics/src/index.rs delete mode 100644 libeir_diagnostics/src/reporting.rs delete mode 100644 libeir_diagnostics/src/reporting/diagnostic.rs delete mode 100644 libeir_diagnostics/src/reporting/emitter.rs create mode 100644 libeir_diagnostics/src/source.rs create mode 100644 libeir_diagnostics/src/span.rs delete mode 100644 libeir_syntax_erl/src/parser/visitor/mod.rs delete mode 100644 libeir_syntax_erl/src/parser/visitor/pretty_print.rs diff --git a/libeir_diagnostics/Cargo.toml b/libeir_diagnostics/Cargo.toml index f032892..df45c82 100644 --- a/libeir_diagnostics/Cargo.toml +++ b/libeir_diagnostics/Cargo.toml @@ -6,11 +6,13 @@ edition = "2018" license = "MIT OR Apache-2.0" [dependencies] -termcolor = "0.3" anyhow = "1.0" thiserror = "1.0" itertools = "0.8" unicode-width = "0.1" +codespan = "0.9" +codespan-reporting = "0.9" +dashmap = "3.11" [dev-dependencies] pretty_assertions = "0.5" diff --git a/libeir_diagnostics/src/codemap.rs b/libeir_diagnostics/src/codemap.rs new file mode 100644 index 0000000..17f2be8 --- /dev/null +++ b/libeir_diagnostics/src/codemap.rs @@ -0,0 +1,156 @@ +use std::ops::Range; +use std::path::PathBuf; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::Arc; + +use dashmap::DashMap; + +use super::*; + +#[derive(Debug)] +pub struct CodeMap { + files: DashMap>, + seen: DashMap, + next_file_id: AtomicU32, +} +impl CodeMap { + /// Creates an empty `CodeMap`. + pub fn new() -> Self { + Self { + files: DashMap::new(), + seen: DashMap::new(), + next_file_id: AtomicU32::new(1), + } + } + + /// Add a file to the map, returning the handle that can be used to + /// refer to it again. + pub fn add(&self, name: impl Into, source: String) -> SourceId { + // De-duplicate real files on add; it _may_ be possible for concurrent + // adds to add the same file more than once, since we're working across + // two maps; but since DashMap uses read/write locks internally to lock + // buckets, the sequence of locks required here should prevent that from + // happening + // + // We don't de-duplicate virtual files, because the same name could be used + // for different content, and its unlikely that we'd be adding the same content + // over and over again with the same virtual file name + let name = name.into(); + if let FileName::Real(ref path) = name { + let seen_ref = self + .seen + .entry(path.clone()) + .or_insert_with(|| self.insert_file(name, source)); + *seen_ref.value() + } else { + self.insert_file(name, source) + } + } + + fn insert_file(&self, name: FileName, source: String) -> SourceId { + let file_id = self.next_file_id(); + self.files.insert( + file_id, + Arc::new(SourceFile::new(file_id, name.into(), source)), + ); + file_id + } + + /// Get the file corresponding to the given id. + pub fn get(&self, file_id: SourceId) -> Option> { + if file_id == SourceId::UNKNOWN { + None + } else { + self.files.get(&file_id).map(|r| r.value().clone()) + } + } + + pub fn name(&self, file_id: SourceId) -> Option { + self.get(file_id).map(|f| f.name().clone()) + } + + pub fn iter<'a>(&'a self) -> impl Iterator> + 'a { + self.files.iter().map(|r| r.value().clone()) + } + + pub fn line_span( + &self, + file_id: SourceId, + line_index: impl Into, + ) -> Option> { + let f = self.get(file_id)?; + Some(f.line_span(line_index.into())) + } + + pub fn line_index( + &self, + file_id: SourceId, + byte_index: impl Into, + ) -> Option { + let f = self.get(file_id)?; + Some(f.line_index(byte_index.into())) + } + + pub fn location( + &self, + file_id: SourceId, + byte_index: impl Into, + ) -> Option> { + let f = self.get(file_id)?; + Some(f.location(byte_index.into())) + } + + pub fn source_span(&self, file_id: SourceId) -> Option { + let f = self.get(file_id)?; + Some(f.source_span()) + } + + pub fn source_slice<'a>( + &'a self, + file_id: SourceId, + span: impl Into, + ) -> Option> { + let f = self.get(file_id)?; + match f.source_slice(span.into()) { + Err(err) => Some(Err(err)), + Ok(slice) => unsafe { Some(Ok(std::mem::transmute::<&str, &'a str>(slice))) }, + } + } + + #[inline(always)] + fn next_file_id(&self) -> SourceId { + let id = self.next_file_id.fetch_add(1, Ordering::Relaxed); + SourceId::new(id) + } +} +impl Default for CodeMap { + fn default() -> Self { + Self::new() + } +} +impl<'a> Files<'a> for CodeMap { + type FileId = SourceId; + type Name = String; + type Source = &'a str; + + fn name(&self, file_id: Self::FileId) -> Option { + Some(format!("{}", self.get(file_id)?.name())) + } + + fn source(&self, file_id: Self::FileId) -> Option<&'a str> { + use std::mem; + + let f = self.get(file_id)?; + Some(unsafe { mem::transmute::<&str, &'a str>(f.source()) }) + } + + fn line_index(&self, file_id: Self::FileId, byte_index: usize) -> Option { + Some(self.line_index(file_id, byte_index as u32)?.to_usize()) + } + + fn line_range(&self, file_id: Self::FileId, line_index: usize) -> Option> { + let span = self.line_span(file_id, line_index as u32)?.ok()?; + + Some(span.start().to_usize()..span.end().to_usize()) + } +} diff --git a/libeir_diagnostics/src/diagnostics.rs b/libeir_diagnostics/src/diagnostics.rs deleted file mode 100644 index db4d957..0000000 --- a/libeir_diagnostics/src/diagnostics.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! This is a forked version of `codespan` and `codespan-reporting` vendored into this project: -//! -//! * [codespan](https://github.com/brendanzab/codespan) -//! -//! Utilities for working with source code and printing nicely formatted -//! diagnostic information like warnings and errors. -mod codemap; -mod filemap; -mod index; -mod span; - -pub use self::codemap::CodeMap; -pub use self::filemap::{ByteIndexError, LineIndexError, LocationError, SpanError}; -pub use self::filemap::{FileMap, FileName}; -pub use self::index::{ByteIndex, ByteOffset}; -pub use self::index::{ColumnIndex, ColumnNumber, ColumnOffset}; -pub use self::index::{Index, Offset}; -pub use self::index::{LineIndex, LineNumber, LineOffset}; -pub use self::index::{RawIndex, RawOffset}; -pub use self::span::{ByteSpan, Span, DUMMY_SPAN}; diff --git a/libeir_diagnostics/src/diagnostics/codemap.rs b/libeir_diagnostics/src/diagnostics/codemap.rs deleted file mode 100644 index 04d982c..0000000 --- a/libeir_diagnostics/src/diagnostics/codemap.rs +++ /dev/null @@ -1,180 +0,0 @@ -use std::io; -use std::path::PathBuf; -use std::sync::Arc; - -use itertools::Itertools; - -use super::filemap::{FileMap, FileName}; -use super::index::{ByteIndex, ByteOffset, RawIndex}; - -#[derive(Clone, Debug, Default)] -pub struct CodeMap { - files: Vec>, -} -impl CodeMap { - /// Creates an empty `CodeMap`. - pub fn new() -> CodeMap { - CodeMap::default() - } - - /// The next start index to use for a new filemap - fn next_start_index(&self) -> ByteIndex { - let end_index = self - .files - .last() - .map(|x| x.span().end()) - .unwrap_or_else(ByteIndex::none); - - // Add one byte of padding between each file - end_index + ByteOffset(1) - } - - /// Adds a filemap to the codemap with the given name and source item - pub fn add_filemap(&mut self, name: FileName, src: S) -> Arc - where - S: AsRef, - { - let file = Arc::new(FileMap::with_index( - name, - src.as_ref().to_owned(), - self.next_start_index(), - )); - self.files.push(file.clone()); - file - } - - /// Adds a filemap to the codemap with the given name and source string - pub fn add_filemap_from_string(&mut self, name: FileName, src: String) -> Arc { - let file = Arc::new(FileMap::with_index(name, src, self.next_start_index())); - self.files.push(file.clone()); - file - } - - /// Adds a filemap to the codemap with the given name and source string - pub fn add_filemap_from_disk>(&mut self, name: P) -> io::Result> { - let file = Arc::new(FileMap::from_disk(name, self.next_start_index())?); - self.files.push(file.clone()); - Ok(file) - } - - /// Looks up the `File` that contains the specified byte index. - pub fn find_file(&self, index: ByteIndex) -> Option<&Arc> { - self.find_index(index).map(|i| &self.files[i]) - } - - pub fn update(&mut self, index: ByteIndex, src: String) -> Option> { - self.find_index(index).map(|i| { - let min = if i == 0 { - ByteIndex(1) - } else { - self.files[i - 1].span().end() + ByteOffset(1) - }; - let max = self - .files - .get(i + 1) - .map_or(ByteIndex(RawIndex::max_value()), |file_map| { - file_map.span().start() - }) - - ByteOffset(1); - if src.len() <= (max - min).to_usize() { - let start_index = self.files[i].span().start(); - let name = self.files[i].name().clone(); - let new_file = Arc::new(FileMap::with_index(name, src, start_index)); - self.files[i] = new_file.clone(); - new_file - } else { - let file = self.files.remove(i); - match self - .files - .first() - .map(|file| file.span().start().to_usize() - 1) - .into_iter() - .chain(self.files.iter().tuple_windows().map(|(x, y)| { - eprintln!("{} {}", x.span(), y.span()); - (y.span().start() - x.span().end()).to_usize() - 1 - })) - .position(|size| size >= src.len() + 1) - { - Some(j) => { - let start_index = if j == 0 { - ByteIndex(1) - } else { - self.files[j - 1].span().end() + ByteOffset(1) - }; - let new_file = - Arc::new(FileMap::with_index(file.name().clone(), src, start_index)); - self.files.insert(j, new_file.clone()); - new_file - } - None => self.add_filemap(file.name().clone(), src), - } - } - }) - } - - pub fn iter(&self) -> impl Iterator> { - self.files.iter() - } - - fn find_index(&self, index: ByteIndex) -> Option { - use std::cmp::Ordering; - - self.files - .binary_search_by(|file| match () { - () if file.span().start() > index => Ordering::Greater, - () if file.span().end() < index => Ordering::Less, - () => Ordering::Equal, - }) - .ok() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::diagnostics::index::{ByteIndex, RawIndex}; - use crate::diagnostics::span::Span; - - fn check_maps(code_map: &CodeMap, files: &[(RawIndex, &str, &str)]) { - println!("{:?}", code_map); - assert_eq!(code_map.files.len(), files.len()); - let mut prev_span = Span::new(0.into(), 0.into()); - for (i, (file, &(start, name, src))) in code_map.files.iter().zip(files).enumerate() { - println!("{}: {:?} <=> {:?}", i, file, (start, name, src)); - match *file.name() { - FileName::Virtual(ref virt) => assert_eq!(*virt, name, "At index {}", i), - _ => panic!(), - } - assert_eq!(ByteIndex(start), file.span().start(), "At index {}", i); - assert!(prev_span.end() < file.span().start(), "At index {}", i); - assert_eq!(file.src(), src, "At index {}", i); - - prev_span = file.span(); - } - } - - #[test] - fn update() { - let mut code_map = CodeMap::new(); - - let a_span = code_map - .add_filemap_from_string("a".into(), "a".into()) - .span(); - let b_span = code_map - .add_filemap_from_string("b".into(), "b".into()) - .span(); - let c_span = code_map - .add_filemap_from_string("c".into(), "c".into()) - .span(); - - code_map.update(a_span.start(), "aa".into()).unwrap(); - check_maps(&code_map, &[(3, "b", "b"), (5, "c", "c"), (7, "a", "aa")]); - - code_map.update(b_span.start(), "".into()).unwrap().span(); - check_maps(&code_map, &[(3, "b", ""), (5, "c", "c"), (7, "a", "aa")]); - - code_map.update(c_span.start(), "ccc".into()).unwrap(); - check_maps(&code_map, &[(3, "b", ""), (7, "a", "aa"), (10, "c", "ccc")]); - } -} diff --git a/libeir_diagnostics/src/diagnostics/filemap.rs b/libeir_diagnostics/src/diagnostics/filemap.rs deleted file mode 100644 index 7c79940..0000000 --- a/libeir_diagnostics/src/diagnostics/filemap.rs +++ /dev/null @@ -1,581 +0,0 @@ -//! Various source mapping utilities - -use std::borrow::Cow; -use std::path::{Path, PathBuf}; -use std::{fmt, io}; - -use thiserror::Error; - -use super::index::{ - ByteIndex, ByteOffset, ColumnIndex, LineIndex, LineOffset, RawIndex, RawOffset, -}; -use super::span::ByteSpan; - -use unicode_width::UnicodeWidthChar; - -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum FileName { - /// A real file on disk - Real(PathBuf), - /// A synthetic file, eg. from the REPL - Virtual(Cow<'static, str>), -} - -impl From for FileName { - fn from(name: PathBuf) -> FileName { - FileName::real(name) - } -} - -impl From for PathBuf { - fn from(name: FileName) -> PathBuf { - match name { - FileName::Real(path) => path, - FileName::Virtual(Cow::Owned(owned)) => PathBuf::from(owned), - FileName::Virtual(Cow::Borrowed(borrowed)) => PathBuf::from(borrowed), - } - } -} - -impl<'a> From<&'a FileName> for &'a Path { - fn from(name: &'a FileName) -> &'a Path { - match *name { - FileName::Real(ref path) => path, - FileName::Virtual(ref cow) => Path::new(cow.as_ref()), - } - } -} - -impl<'a> From<&'a Path> for FileName { - fn from(name: &Path) -> FileName { - FileName::real(name) - } -} - -impl From for FileName { - fn from(name: String) -> FileName { - FileName::virtual_(name) - } -} - -impl From<&'static str> for FileName { - fn from(name: &'static str) -> FileName { - FileName::virtual_(name) - } -} - -impl AsRef for FileName { - fn as_ref(&self) -> &Path { - match *self { - FileName::Real(ref path) => path.as_ref(), - FileName::Virtual(ref cow) => Path::new(cow.as_ref()), - } - } -} - -impl PartialEq for FileName { - fn eq(&self, other: &Path) -> bool { - self.as_ref() == other - } -} - -impl PartialEq for FileName { - fn eq(&self, other: &PathBuf) -> bool { - self.as_ref() == other.as_path() - } -} - -impl FileName { - pub fn real>(name: T) -> FileName { - FileName::Real(name.into()) - } - - pub fn virtual_>>(name: T) -> FileName { - FileName::Virtual(name.into()) - } - - pub fn to_string(&self) -> String { - match *self { - FileName::Real(ref path) => match path.to_str() { - None => path.to_string_lossy().into_owned(), - Some(s) => s.to_owned(), - }, - FileName::Virtual(ref s) => s.clone().into_owned(), - } - } -} - -impl fmt::Display for FileName { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - FileName::Real(ref path) => write!(fmt, "{}", path.display()), - FileName::Virtual(ref name) => write!(fmt, "<{}>", name), - } - } -} - -#[derive(Error, Debug, PartialEq)] -pub enum LineIndexError { - #[error("Line out of bounds - given: {given:?}, max: {max:?}")] - OutOfBounds { given: LineIndex, max: LineIndex }, -} - -#[derive(Error, Debug, PartialEq)] -pub enum ByteIndexError { - #[error("Byte index out of bounds - given: {given:?}, span: {span:?}")] - OutOfBounds { given: ByteIndex, span: ByteSpan }, - #[error("Byte index points within a character boundary - given: {given:?}")] - InvalidCharBoundary { given: ByteIndex }, -} - -#[derive(Error, Debug, PartialEq)] -pub enum LocationError { - #[error("Line out of bounds - given: {given:?}, max: {max:?}")] - LineOutOfBounds { given: LineIndex, max: LineIndex }, - #[error("Column out of bounds - given: {given:?}, max: {max:?}")] - ColumnOutOfBounds { - given: ColumnIndex, - max: ColumnIndex, - }, -} - -#[derive(Error, Debug, PartialEq)] -pub enum SpanError { - #[error("Span out of bounds - given: {given:?}, span: {span:?}")] - OutOfBounds { given: ByteSpan, span: ByteSpan }, -} - -#[derive(Debug)] -/// Some source code -pub struct FileMap { - /// The name of the file that the source came from - pub name: FileName, - /// The complete source code - pub src: String, - /// The span of the source in the `CodeMap` - span: ByteSpan, - /// Offsets to the line beginnings in the source - lines: Vec, -} - -impl FileMap { - /// Read some source code from a file, loading it into a filemap - pub(crate) fn from_disk>(name: P, start: ByteIndex) -> io::Result { - use std::fs::File; - use std::io::Read; - - let name = name.into(); - let mut file = File::open(&name)?; - let mut src = String::new(); - file.read_to_string(&mut src)?; - - Ok(FileMap::with_index(FileName::Real(name), src, start)) - } - - /// Construct a new, standalone filemap. - /// - /// This can be useful for tests that consist of a single source file. Production code should - /// however use `CodeMap::add_filemap` or `CodeMap::add_filemap_from_disk` instead. - pub fn new>(name: FileName, src: S) -> FileMap { - FileMap::with_index(name, src, ByteIndex(1)) - } - - pub(super) fn with_index(name: FileName, src: S, start: ByteIndex) -> FileMap - where - S: AsRef, - { - use std::iter; - - let src = src.as_ref(); - let span = ByteSpan::from_offset(start, ByteOffset::from_str(src)); - let lines = { - let newline_off = ByteOffset::from_char_utf8('\n'); - let offsets = src - .match_indices('\n') - .map(|(i, _)| ByteOffset(i as RawOffset) + newline_off); - - iter::once(ByteOffset(0)).chain(offsets).collect() - }; - - FileMap { - name, - src: src.to_owned(), - span, - lines, - } - } - - /// The name of the file that the source came from - pub fn name(&self) -> &FileName { - &self.name - } - - /// The underlying source code - pub fn src(&self) -> &str { - &self.src.as_ref() - } - - /// The span of the source in the `CodeMap` - pub fn span(&self) -> ByteSpan { - self.span - } - - pub fn offset( - &self, - line: LineIndex, - column: ColumnIndex, - ) -> Result { - self.byte_index(line, column) - .map(|index| index - self.span.start()) - } - - pub fn byte_index( - &self, - line: LineIndex, - column: ColumnIndex, - ) -> Result { - self.line_span(line) - .map_err( - |LineIndexError::OutOfBounds { given, max }| LocationError::LineOutOfBounds { - given, - max, - }, - ) - .and_then(|span| { - let distance = ColumnIndex(span.end().0 - span.start().0); - if column > distance { - Err(LocationError::ColumnOutOfBounds { - given: column, - max: distance, - }) - } else { - Ok(span.start() + ByteOffset::from(column.0 as i64)) - } - }) - } - - /// Returns the byte offset to the start of `line`. - /// - /// Lines may be delimited with either `\n` or `\r\n`. - pub fn line_offset(&self, index: LineIndex) -> Result { - self.lines - .get(index.to_usize()) - .cloned() - .ok_or_else(|| LineIndexError::OutOfBounds { - given: index, - max: LineIndex(self.lines.len() as RawIndex - 1), - }) - } - - /// Returns the byte index of the start of `line`. - /// - /// Lines may be delimited with either `\n` or `\r\n`. - pub fn line_byte_index(&self, index: LineIndex) -> Result { - self.line_offset(index) - .map(|offset| self.span.start() + offset) - } - - /// Returns the byte offset to the start of `line`. - /// - /// Lines may be delimited with either `\n` or `\r\n`. - pub fn line_span(&self, line: LineIndex) -> Result { - let start = self.span.start() + self.line_offset(line)?; - let end = match self.line_offset(line + LineOffset(1)) { - Ok(offset_hi) => self.span.start() + offset_hi, - Err(_) => self.span.end(), - }; - - Ok(ByteSpan::new(end, start)) - } - - /// Returns the line and column location of `byte` - /// TODO - pub fn location(&self, index: ByteIndex) -> Result<(LineIndex, ColumnIndex), ByteIndexError> { - let line_index = self.find_line(index)?; - let line_span = self.line_span(line_index).unwrap(); // line_index should be valid! - let line_slice = self.src_slice(line_span).unwrap(); // line_span should be valid! - let byte_col = index - line_span.start(); - let mut column_i = 0; - for c in line_slice[..byte_col.to_usize()].chars() { - match c.width() { - None => continue, - Some(w) => column_i = column_i + w, - } - } - let column_index = ColumnIndex(column_i as RawIndex); - - Ok((line_index, column_index)) - } - - /// Returns the line index that the byte index points to - pub fn find_line(&self, index: ByteIndex) -> Result { - if index < self.span.start() || index > self.span.end() { - Err(ByteIndexError::OutOfBounds { - given: index, - span: self.span, - }) - } else { - let offset = index - self.span.start(); - - if self.src.as_str().is_char_boundary(offset.to_usize()) { - match self.lines.binary_search(&offset) { - Ok(i) => Ok(LineIndex(i as RawIndex)), - Err(i) => Ok(LineIndex(i as RawIndex - 1)), - } - } else { - Err(ByteIndexError::InvalidCharBoundary { - given: self.span.start(), - }) - } - } - } - - /// Get the corresponding source string for a span - /// - /// Returns `Err` if the span is outside the bounds of the file - pub fn src_slice(&self, span: ByteSpan) -> Result<&str, SpanError> { - if self.span.contains(span) { - let start = (span.start() - self.span.start()).to_usize(); - let end = (span.end() - self.span.start()).to_usize(); - - // TODO: check char boundaries - Ok(&self.src.as_str()[start..end]) - } else { - Err(SpanError::OutOfBounds { - given: span, - span: self.span, - }) - } - } -} - -#[cfg(test)] -mod tests { - use crate::diagnostics::codemap::CodeMap; - use pretty_assertions::assert_eq; - use std::sync::Arc; - - use super::*; - - struct TestData { - filemap: Arc, - lines: &'static [&'static str], - } - - impl TestData { - fn new() -> TestData { - let mut codemap = CodeMap::new(); - let lines = &[ - "hello!\n", - "howdy\n", - "\r\n", - "hi萤\n", - "bloop\n", - "goopey\r\n", - ]; - let filemap = codemap.add_filemap(FileName::Virtual("test".into()), lines.concat()); - - TestData { filemap, lines } - } - - fn byte_offsets(&self) -> Vec { - let mut offset = ByteOffset(0); - let mut byte_offsets = Vec::new(); - - for line in self.lines { - byte_offsets.push(offset); - offset += ByteOffset::from_str(line); - - let line_end = if line.ends_with("\r\n") { - offset + -ByteOffset::from_char_utf8('\r') + -ByteOffset::from_char_utf8('\n') - } else if line.ends_with("\n") { - offset + -ByteOffset::from_char_utf8('\n') - } else { - offset - }; - - byte_offsets.push(line_end); - } - - // bump us past the end - byte_offsets.push(offset); - - byte_offsets - } - - fn byte_indices(&self) -> Vec { - let mut offsets = vec![ByteIndex::none()]; - offsets.extend(self.byte_offsets().iter().map(|&off| ByteIndex(1) + off)); - let out_of_bounds = *offsets.last().unwrap() + ByteOffset(1); - offsets.push(out_of_bounds); - offsets - } - - fn line_indices(&self) -> Vec { - (0..self.lines.len() + 2) - .map(|i| LineIndex(i as RawIndex)) - .collect() - } - } - - #[test] - fn offset() { - let test_data = TestData::new(); - assert!(test_data - .filemap - .offset( - (test_data.lines.len() as u32 - 1).into(), - (test_data.lines.last().unwrap().len() as u32).into() - ) - .is_ok()); - assert!(test_data - .filemap - .offset( - (test_data.lines.len() as u32 - 1).into(), - (test_data.lines.last().unwrap().len() as u32 + 1).into() - ) - .is_err()); - } - - #[test] - fn line_offset() { - let test_data = TestData::new(); - let offsets: Vec<_> = test_data - .line_indices() - .iter() - .map(|&i| test_data.filemap.line_offset(i)) - .collect(); - - assert_eq!( - offsets, - vec![ - Ok(ByteOffset(0)), - Ok(ByteOffset(7)), - Ok(ByteOffset(13)), - Ok(ByteOffset(15)), - Ok(ByteOffset(21)), - Ok(ByteOffset(27)), - Ok(ByteOffset(35)), - Err(LineIndexError::OutOfBounds { - given: LineIndex(7), - max: LineIndex(6), - }), - ], - ); - } - - #[test] - fn line_byte_index() { - let test_data = TestData::new(); - let offsets: Vec<_> = test_data - .line_indices() - .iter() - .map(|&i| test_data.filemap.line_byte_index(i)) - .collect(); - - assert_eq!( - offsets, - vec![ - Ok(test_data.filemap.span().start() + ByteOffset(0)), - Ok(test_data.filemap.span().start() + ByteOffset(7)), - Ok(test_data.filemap.span().start() + ByteOffset(13)), - Ok(test_data.filemap.span().start() + ByteOffset(15)), - Ok(test_data.filemap.span().start() + ByteOffset(21)), - Ok(test_data.filemap.span().start() + ByteOffset(27)), - Ok(test_data.filemap.span().start() + ByteOffset(35)), - Err(LineIndexError::OutOfBounds { - given: LineIndex(7), - max: LineIndex(6), - }), - ], - ); - } - - // #[test] - // fn line_span() { - // let filemap = filemap(); - // let start = filemap.span().start(); - - // assert_eq!(filemap.line_byte_index(Li(0)), Some(start + BOff(0))); - // assert_eq!(filemap.line_byte_index(Li(1)), Some(start + BOff(7))); - // assert_eq!(filemap.line_byte_index(Li(2)), Some(start + BOff(13))); - // assert_eq!(filemap.line_byte_index(Li(3)), Some(start + BOff(14))); - // assert_eq!(filemap.line_byte_index(Li(4)), Some(start + BOff(20))); - // assert_eq!(filemap.line_byte_index(Li(5)), Some(start + BOff(26))); - // assert_eq!(filemap.line_byte_index(Li(6)), None); - // } - - #[test] - fn location() { - let test_data = TestData::new(); - let lines: Vec<_> = test_data - .byte_indices() - .iter() - .map(|&index| test_data.filemap.location(index)) - .collect(); - - assert_eq!( - lines, - vec![ - Err(ByteIndexError::OutOfBounds { - given: ByteIndex(0), - span: test_data.filemap.span(), - }), - Ok((LineIndex(0), ColumnIndex(0))), - Ok((LineIndex(0), ColumnIndex(6))), - Ok((LineIndex(1), ColumnIndex(0))), - Ok((LineIndex(1), ColumnIndex(5))), - Ok((LineIndex(2), ColumnIndex(0))), - Ok((LineIndex(2), ColumnIndex(0))), - Ok((LineIndex(3), ColumnIndex(0))), - Ok((LineIndex(3), ColumnIndex(4))), - Ok((LineIndex(4), ColumnIndex(0))), - Ok((LineIndex(4), ColumnIndex(5))), - Ok((LineIndex(5), ColumnIndex(0))), - Ok((LineIndex(5), ColumnIndex(6))), - Ok((LineIndex(6), ColumnIndex(0))), - Err(ByteIndexError::OutOfBounds { - given: ByteIndex(37), - span: test_data.filemap.span() - }), - ], - ); - } - - #[test] - fn find_line() { - let test_data = TestData::new(); - let lines: Vec<_> = test_data - .byte_indices() - .iter() - .map(|&index| test_data.filemap.find_line(index)) - .collect(); - - assert_eq!( - lines, - vec![ - Err(ByteIndexError::OutOfBounds { - given: ByteIndex(0), - span: test_data.filemap.span(), - }), - Ok(LineIndex(0)), - Ok(LineIndex(0)), - Ok(LineIndex(1)), - Ok(LineIndex(1)), - Ok(LineIndex(2)), - Ok(LineIndex(2)), - Ok(LineIndex(3)), - Ok(LineIndex(3)), - Ok(LineIndex(4)), - Ok(LineIndex(4)), - Ok(LineIndex(5)), - Ok(LineIndex(5)), - Ok(LineIndex(6)), - Err(ByteIndexError::OutOfBounds { - given: ByteIndex(37), - span: test_data.filemap.span(), - }), - ], - ); - } -} diff --git a/libeir_diagnostics/src/diagnostics/index.rs b/libeir_diagnostics/src/diagnostics/index.rs deleted file mode 100644 index 08e9acd..0000000 --- a/libeir_diagnostics/src/diagnostics/index.rs +++ /dev/null @@ -1,349 +0,0 @@ -//! Wrapper types that specify positions in a source file - -use std::fmt; -use std::ops::{Add, AddAssign, Neg, Sub, SubAssign}; - -/// The raw, untyped index -pub type RawIndex = u32; - -/// The raw, untyped offset -pub type RawOffset = i64; - -/// A zero-indexed line offset into a source file -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct LineIndex(pub RawIndex); -impl LineIndex { - /// The 1-indexed line number. Useful for pretty printing source locations. - pub fn number(self) -> LineNumber { - LineNumber(self.0 + 1) - } - - /// Convert the index into a `usize`, for use in array indexing - pub fn to_usize(self) -> usize { - self.0 as usize - } -} -impl Default for LineIndex { - fn default() -> LineIndex { - LineIndex(0) - } -} -impl fmt::Debug for LineIndex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "LineIndex({})", self.0) - } -} - -/// A 1-indexed line number. Useful for pretty printing source locations. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct LineNumber(RawIndex); -impl LineNumber { - pub fn to_usize(self) -> usize { - self.0 as usize - } -} -impl fmt::Debug for LineNumber { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "LineNumber({})", self.0) - } -} -impl fmt::Display for LineNumber { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// A line offset in a source file -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct LineOffset(pub RawOffset); -impl Default for LineOffset { - fn default() -> LineOffset { - LineOffset(0) - } -} -impl fmt::Debug for LineOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} -impl fmt::Display for LineOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// A zero-indexed column offset into a source file -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct ColumnIndex(pub RawIndex); -impl ColumnIndex { - /// The 1-indexed column number. Useful for pretty printing source locations. - #[inline] - pub fn number(self) -> ColumnNumber { - ColumnNumber(self.0 + 1) - } - - /// Convert the index into a `usize`, for use in array indexing - #[inline] - pub fn to_usize(self) -> usize { - self.0 as usize - } -} -impl Default for ColumnIndex { - fn default() -> ColumnIndex { - ColumnIndex(0) - } -} -impl fmt::Debug for ColumnIndex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ColumnIndex({})", self.0) - } -} - -/// A 1-indexed column number. Useful for pretty printing source locations. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct ColumnNumber(RawIndex); -impl ColumnNumber { - pub fn to_usize(self) -> usize { - self.0 as usize - } -} -impl fmt::Debug for ColumnNumber { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ColumnNumber({})", self.0) - } -} -impl fmt::Display for ColumnNumber { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// A column offset in a source file -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct ColumnOffset(pub RawOffset); -impl Default for ColumnOffset { - fn default() -> ColumnOffset { - ColumnOffset(0) - } -} -impl fmt::Debug for ColumnOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ColumnOffset({})", self.0) - } -} -impl fmt::Display for ColumnOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// A byte position in a source file -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct ByteIndex(pub RawIndex); -impl ByteIndex { - /// A byte position that will never point to a valid file - pub fn none() -> ByteIndex { - ByteIndex(0) - } - - /// Convert the position into a `usize`, for use in array indexing - #[inline] - pub fn to_usize(self) -> usize { - self.0 as usize - } -} -impl Default for ByteIndex { - fn default() -> ByteIndex { - ByteIndex(0) - } -} -impl fmt::Debug for ByteIndex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ByteIndex({})", self.0) - } -} -impl fmt::Display for ByteIndex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", 0) - } -} - -/// A byte offset in a source file -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct ByteOffset(pub RawOffset); - -impl ByteOffset { - /// Create a byte offset from a UTF8-encoded character - #[inline] - pub fn from_char_utf8(ch: char) -> ByteOffset { - ByteOffset(ch.len_utf8() as RawOffset) - } - - /// Create a byte offset from a UTF- encoded string - #[inline] - pub fn from_str(value: &str) -> ByteOffset { - ByteOffset(value.len() as RawOffset) - } - - /// Convert the offset into a `usize`, for use in array indexing - #[inline] - pub fn to_usize(self) -> usize { - self.0 as usize - } -} -impl Default for ByteOffset { - #[inline] - fn default() -> ByteOffset { - ByteOffset(0) - } -} -impl fmt::Debug for ByteOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ByteOffset({})", self.0) - } -} -impl fmt::Display for ByteOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// A relative offset between two indices -/// -/// These can be thought of as 1-dimensional vectors -pub trait Offset: Copy + Ord -where - Self: Neg, - Self: Add, - Self: AddAssign, - Self: Sub, - Self: SubAssign, -{ - const ZERO: Self; -} - -/// Index types -/// -/// These can be thought of as 1-dimensional points -pub trait Index: Copy + Ord -where - Self: Add<::Offset, Output = Self>, - Self: AddAssign<::Offset>, - Self: Sub<::Offset, Output = Self>, - Self: SubAssign<::Offset>, - Self: Sub::Offset>, -{ - type Offset: Offset; -} - -macro_rules! impl_index { - ($Index:ident, $Offset:ident) => { - impl From for $Offset { - #[inline] - fn from(i: RawOffset) -> Self { - $Offset(i) - } - } - - impl From for $Index { - #[inline] - fn from(i: RawIndex) -> Self { - $Index(i) - } - } - - impl Offset for $Offset { - const ZERO: $Offset = $Offset(0); - } - - impl Index for $Index { - type Offset = $Offset; - } - - impl Add<$Offset> for $Index { - type Output = $Index; - - #[inline] - fn add(self, rhs: $Offset) -> $Index { - $Index((self.0 as RawOffset + rhs.0) as RawIndex) - } - } - - impl AddAssign<$Offset> for $Index { - #[inline] - fn add_assign(&mut self, rhs: $Offset) { - *self = *self + rhs; - } - } - - impl Neg for $Offset { - type Output = $Offset; - - #[inline] - fn neg(self) -> $Offset { - $Offset(-self.0) - } - } - - impl Add<$Offset> for $Offset { - type Output = $Offset; - - #[inline] - fn add(self, rhs: $Offset) -> $Offset { - $Offset(self.0 + rhs.0) - } - } - - impl AddAssign<$Offset> for $Offset { - #[inline] - fn add_assign(&mut self, rhs: $Offset) { - self.0 += rhs.0; - } - } - - impl Sub<$Offset> for $Offset { - type Output = $Offset; - - #[inline] - fn sub(self, rhs: $Offset) -> $Offset { - $Offset(self.0 - rhs.0) - } - } - - impl SubAssign<$Offset> for $Offset { - #[inline] - fn sub_assign(&mut self, rhs: $Offset) { - self.0 -= rhs.0; - } - } - - impl Sub for $Index { - type Output = $Offset; - - #[inline] - fn sub(self, rhs: $Index) -> $Offset { - $Offset(self.0 as RawOffset - rhs.0 as RawOffset) - } - } - - impl Sub<$Offset> for $Index { - type Output = $Index; - - #[inline] - fn sub(self, rhs: $Offset) -> $Index { - $Index((self.0 as RawOffset - rhs.0 as RawOffset) as u32) - } - } - - impl SubAssign<$Offset> for $Index { - #[inline] - fn sub_assign(&mut self, rhs: $Offset) { - self.0 = (self.0 as RawOffset - rhs.0) as RawIndex; - } - } - }; -} - -impl_index!(LineIndex, LineOffset); -impl_index!(ColumnIndex, ColumnOffset); -impl_index!(ByteIndex, ByteOffset); diff --git a/libeir_diagnostics/src/diagnostics/span.rs b/libeir_diagnostics/src/diagnostics/span.rs deleted file mode 100644 index 35eaa99..0000000 --- a/libeir_diagnostics/src/diagnostics/span.rs +++ /dev/null @@ -1,288 +0,0 @@ -use std::cmp::Ordering; -use std::{cmp, fmt}; - -use super::index::{ByteIndex, Index}; - -/// A region of code in a source file -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub struct Span { - start: I, - end: I, -} -impl Span { - /// Create a new span - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let span = Span::new(ByteIndex(3), ByteIndex(6)); - /// assert_eq!(span.start(), ByteIndex(3)); - /// assert_eq!(span.end(), ByteIndex(6)); - /// ``` - /// - /// `start` and `end` are reordered to maintain the invariant that `start <= end` - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let span = Span::new(ByteIndex(6), ByteIndex(3)); - /// assert_eq!(span.start(), ByteIndex(3)); - /// assert_eq!(span.end(), ByteIndex(6)); - /// ``` - #[inline] - pub fn new(start: I, end: I) -> Span { - if start <= end { - Span { start, end } - } else { - Span { - start: end, - end: start, - } - } - } - - #[inline] - pub fn map(self, mut f: F) -> Span - where - F: FnMut(I) -> J, - J: Ord, - { - Span::new(f(self.start), f(self.end)) - } -} -impl Span { - /// Get the start index - #[inline] - pub fn start(self) -> I { - self.start - } - - /// Get the end index - #[inline] - pub fn end(self) -> I { - self.end - } -} -impl Span { - /// Makes a span from offsets relative to the start of this span. - #[inline] - pub fn subspan(&self, begin: I::Offset, end: I::Offset) -> Span { - debug_assert!(end >= begin); - debug_assert!(self.start() + end <= self.end()); - Span { - start: self.start() + begin, - end: self.start() + end, - } - } - - /// Create a new span from this one by shrinking the front of the span, i.e. increasing it by - /// some offset - #[inline] - pub fn shrink_front(self, shift: I::Offset) -> Span { - Span::new(self.start() + shift, self.end()) - } - - /// Create a new span from a byte start and an offset - #[inline] - pub fn from_offset(start: I, off: I::Offset) -> Span { - Span::new(start, start + off) - } - - /// Return a new span with the low byte position replaced with the supplied byte position - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let span = Span::new(ByteIndex(3), ByteIndex(6)); - /// assert_eq!( - /// span.with_start(ByteIndex(2)), - /// Span::new(ByteIndex(2), ByteIndex(6)) - /// ); - /// assert_eq!( - /// span.with_start(ByteIndex(5)), - /// Span::new(ByteIndex(5), ByteIndex(6)) - /// ); - /// assert_eq!( - /// span.with_start(ByteIndex(7)), - /// Span::new(ByteIndex(6), ByteIndex(7)) - /// ); - /// ``` - #[inline] - pub fn with_start(self, start: I) -> Span { - Span::new(start, self.end()) - } - - /// Return a new span with the high byte position replaced with the supplied byte position - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let span = Span::new(ByteIndex(3), ByteIndex(6)); - /// assert_eq!( - /// span.with_end(ByteIndex(7)), - /// Span::new(ByteIndex(3), ByteIndex(7)) - /// ); - /// assert_eq!( - /// span.with_end(ByteIndex(5)), - /// Span::new(ByteIndex(3), ByteIndex(5)) - /// ); - /// assert_eq!( - /// span.with_end(ByteIndex(2)), - /// Span::new(ByteIndex(2), ByteIndex(3)) - /// ); - /// ``` - #[inline] - pub fn with_end(self, end: I) -> Span { - Span::new(self.start(), end) - } - - /// Return true if `self` fully encloses `other`. - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let a = Span::new(ByteIndex(5), ByteIndex(8)); - /// - /// assert_eq!(a.contains(a), true); - /// assert_eq!(a.contains(Span::new(ByteIndex(6), ByteIndex(7))), true); - /// assert_eq!(a.contains(Span::new(ByteIndex(6), ByteIndex(10))), false); - /// assert_eq!(a.contains(Span::new(ByteIndex(3), ByteIndex(6))), false); - /// ``` - #[inline] - pub fn contains(self, other: Span) -> bool { - self.start() <= other.start() && other.end() <= self.end() - } - - /// Return `Equal` if `self` contains `pos`, otherwise it returns `Less` if `pos` is before - /// `start` or `Greater` if `pos` is after or at `end`. - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// use std::cmp::Ordering::*; - /// - /// let a = Span::new(ByteIndex(5), ByteIndex(8)); - /// - /// assert_eq!(a.containment(ByteIndex(4)), Less); - /// assert_eq!(a.containment(ByteIndex(5)), Equal); - /// assert_eq!(a.containment(ByteIndex(6)), Equal); - /// assert_eq!(a.containment(ByteIndex(8)), Equal); - /// assert_eq!(a.containment(ByteIndex(9)), Greater); - /// ``` - #[inline] - pub fn containment(self, pos: I) -> Ordering { - use std::cmp::Ordering::*; - - match (pos.cmp(&self.start), pos.cmp(&self.end)) { - (Equal, _) | (_, Equal) | (Greater, Less) => Equal, - (Less, _) => Less, - (_, Greater) => Greater, - } - } - - /// Return `Equal` if `self` contains `pos`, otherwise it returns `Less` if `pos` is before - /// `start` or `Greater` if `pos` is *strictly* after `end`. - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// use std::cmp::Ordering::*; - /// - /// let a = Span::new(ByteIndex(5), ByteIndex(8)); - /// - /// assert_eq!(a.containment_exclusive(ByteIndex(4)), Less); - /// assert_eq!(a.containment_exclusive(ByteIndex(5)), Equal); - /// assert_eq!(a.containment_exclusive(ByteIndex(6)), Equal); - /// assert_eq!(a.containment_exclusive(ByteIndex(8)), Greater); - /// assert_eq!(a.containment_exclusive(ByteIndex(9)), Greater); - /// ``` - #[inline] - pub fn containment_exclusive(self, pos: I) -> Ordering { - if self.end == pos { - Ordering::Greater - } else { - self.containment(pos) - } - } - - /// Return a `Span` that would enclose both `self` and `end`. - /// - /// ```plain - /// self ~~~~~~~ - /// end ~~~~~~~~ - /// returns ~~~~~~~~~~~~~~~~~~~~~~~ - /// ``` - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let a = Span::new(ByteIndex(2), ByteIndex(5)); - /// let b = Span::new(ByteIndex(10), ByteIndex(14)); - /// - /// assert_eq!(a.to(b), Span::new(ByteIndex(2), ByteIndex(14))); - /// ``` - #[inline] - pub fn to(self, end: Span) -> Span { - Span::new( - cmp::min(self.start(), end.start()), - cmp::max(self.end(), end.end()), - ) - } - - /// Return a `Span` between the end of `self` to the beginning of `end`. - /// - /// ```plain - /// self ~~~~~~~ - /// end ~~~~~~~~ - /// returns ~~~~~~~~~ - /// ``` - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let a = Span::new(ByteIndex(2), ByteIndex(5)); - /// let b = Span::new(ByteIndex(10), ByteIndex(14)); - /// - /// assert_eq!(a.between(b), Span::new(ByteIndex(5), ByteIndex(10))); - /// ``` - #[inline] - pub fn between(self, end: Span) -> Span { - Span::new(self.end(), end.start()) - } - - /// Return a `Span` between the beginning of `self` to the beginning of `end`. - /// - /// ```plain - /// self ~~~~~~~ - /// end ~~~~~~~~ - /// returns ~~~~~~~~~~~~~~~~ - /// ``` - /// - /// ```rust - /// use libeir_diagnostics::{ByteIndex, Span}; - /// - /// let a = Span::new(ByteIndex(2), ByteIndex(5)); - /// let b = Span::new(ByteIndex(10), ByteIndex(14)); - /// - /// assert_eq!(a.until(b), Span::new(ByteIndex(2), ByteIndex(10))); - /// ``` - #[inline] - pub fn until(self, end: Span) -> Span { - Span::new(self.start(), end.start()) - } -} - -impl fmt::Display for Span { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.start.fmt(f)?; - write!(f, "..")?; - self.end.fmt(f)?; - Ok(()) - } -} - -/// A span of byte indices -pub type ByteSpan = Span; - -pub const DUMMY_SPAN: Span = Span { - start: ByteIndex(0), - end: ByteIndex(0), -}; diff --git a/libeir_diagnostics/src/filename.rs b/libeir_diagnostics/src/filename.rs new file mode 100644 index 0000000..95943e1 --- /dev/null +++ b/libeir_diagnostics/src/filename.rs @@ -0,0 +1,106 @@ +//! Various source mapping utilities + +use std::borrow::Cow; +use std::fmt; +use std::path::{Path, PathBuf}; + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum FileName { + /// A real file on disk + Real(PathBuf), + /// A synthetic file, eg. from the REPL + Virtual(Cow<'static, str>), +} + +impl From for FileName { + fn from(name: PathBuf) -> FileName { + FileName::real(name) + } +} + +impl From for PathBuf { + fn from(name: FileName) -> PathBuf { + match name { + FileName::Real(path) => path, + FileName::Virtual(Cow::Owned(owned)) => PathBuf::from(owned), + FileName::Virtual(Cow::Borrowed(borrowed)) => PathBuf::from(borrowed), + } + } +} + +impl<'a> From<&'a FileName> for &'a Path { + fn from(name: &'a FileName) -> &'a Path { + match *name { + FileName::Real(ref path) => path, + FileName::Virtual(ref cow) => Path::new(cow.as_ref()), + } + } +} + +impl<'a> From<&'a Path> for FileName { + fn from(name: &Path) -> FileName { + FileName::real(name) + } +} + +impl From for FileName { + fn from(name: String) -> FileName { + FileName::virtual_(name) + } +} + +impl From<&'static str> for FileName { + fn from(name: &'static str) -> FileName { + FileName::virtual_(name) + } +} + +impl AsRef for FileName { + fn as_ref(&self) -> &Path { + match *self { + FileName::Real(ref path) => path.as_ref(), + FileName::Virtual(ref cow) => Path::new(cow.as_ref()), + } + } +} + +impl PartialEq for FileName { + fn eq(&self, other: &Path) -> bool { + self.as_ref() == other + } +} + +impl PartialEq for FileName { + fn eq(&self, other: &PathBuf) -> bool { + self.as_ref() == other.as_path() + } +} + +impl FileName { + pub fn real>(name: T) -> FileName { + FileName::Real(name.into()) + } + + pub fn virtual_>>(name: T) -> FileName { + FileName::Virtual(name.into()) + } + + pub fn to_string(&self) -> String { + match *self { + FileName::Real(ref path) => match path.to_str() { + None => path.to_string_lossy().into_owned(), + Some(s) => s.to_owned(), + }, + FileName::Virtual(ref s) => s.clone().into_owned(), + } + } +} + +impl fmt::Display for FileName { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + FileName::Real(ref path) => write!(fmt, "{}", path.display()), + FileName::Virtual(ref name) => write!(fmt, "<{}>", name), + } + } +} diff --git a/libeir_diagnostics/src/index.rs b/libeir_diagnostics/src/index.rs new file mode 100644 index 0000000..1395226 --- /dev/null +++ b/libeir_diagnostics/src/index.rs @@ -0,0 +1,108 @@ +use std::num::NonZeroUsize; +use std::ops::{Add, AddAssign, Sub, SubAssign}; + +use codespan::{ByteIndex, ByteOffset, RawIndex, RawOffset}; + +use super::SourceId; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SourceIndex(NonZeroUsize); +impl SourceIndex { + const INDEX_MASK: usize = u32::max_value() as usize; + + const UNKNOWN_SRC_ID: usize = (SourceId::UNKNOWN_SOURCE_ID as usize) << 32; + + pub const UNKNOWN: Self = Self(unsafe { NonZeroUsize::new_unchecked(Self::UNKNOWN_SRC_ID) }); + + #[inline] + pub fn new(source: SourceId, index: ByteIndex) -> Self { + let source = (source.get() as usize) << 32; + + Self(NonZeroUsize::new(source | index.0 as usize).unwrap()) + } + + #[inline] + pub fn source_id(&self) -> SourceId { + SourceId::new((self.0.get() >> 32) as u32) + } + + #[inline] + pub fn index(&self) -> ByteIndex { + ByteIndex((self.0.get() & Self::INDEX_MASK) as u32) + } + + pub fn to_usize(&self) -> usize { + self.0.get() + } +} +impl Default for SourceIndex { + fn default() -> Self { + Self::UNKNOWN + } +} + +impl Add for SourceIndex { + type Output = SourceIndex; + + #[inline] + fn add(self, rhs: usize) -> Self { + if self == Self::UNKNOWN { + return Self::UNKNOWN; + } + let source = self.source_id(); + let index = self.index(); + let new_index = index.0 as RawOffset + rhs as RawOffset; + Self::new(source, ByteIndex(new_index as RawIndex)) + } +} + +impl Add for SourceIndex { + type Output = SourceIndex; + + #[inline] + fn add(self, rhs: ByteOffset) -> Self { + if self == Self::UNKNOWN { + return Self::UNKNOWN; + } + let source = self.source_id(); + let index = self.index(); + let new_index = ByteIndex(index.0) + rhs; + Self::new(source, new_index) + } +} + +impl AddAssign for SourceIndex { + #[inline] + fn add_assign(&mut self, rhs: usize) { + *self = *self + rhs; + } +} + +impl AddAssign for SourceIndex { + #[inline] + fn add_assign(&mut self, rhs: ByteOffset) { + *self = *self + rhs; + } +} + +impl Sub for SourceIndex { + type Output = SourceIndex; + + #[inline] + fn sub(self, rhs: usize) -> Self { + if self == Self::UNKNOWN { + return Self::UNKNOWN; + } + let source = self.source_id(); + let index = self.index(); + let new_index = index.0 as RawOffset - rhs as RawOffset; + Self::new(source, ByteIndex(new_index as RawIndex)) + } +} + +impl SubAssign for SourceIndex { + #[inline] + fn sub_assign(&mut self, rhs: usize) { + *self = *self - rhs; + } +} diff --git a/libeir_diagnostics/src/lib.rs b/libeir_diagnostics/src/lib.rs index 06da976..95aab84 100644 --- a/libeir_diagnostics/src/lib.rs +++ b/libeir_diagnostics/src/lib.rs @@ -1,5 +1,31 @@ -mod diagnostics; -mod reporting; +#![feature(crate_visibility_modifier)] -pub use self::diagnostics::*; -pub use self::reporting::*; +mod codemap; +mod filename; +mod index; +mod source; +mod span; + +pub use codespan::{ByteIndex, ByteOffset}; +pub use codespan::{ColumnIndex, ColumnNumber, ColumnOffset}; +pub use codespan::{Index, Offset}; +pub use codespan::{LineIndex, LineNumber, LineOffset}; +pub use codespan::{LineIndexOutOfBoundsError, LocationError, SpanOutOfBoundsError}; +pub use codespan::{Location, Span}; +pub use codespan::{RawIndex, RawOffset}; + +pub use codespan_reporting::diagnostic::{Label, LabelStyle, Severity}; +pub use codespan_reporting::files::Files; +pub use codespan_reporting::term; + +pub use self::codemap::CodeMap; +pub use self::filename::FileName; +pub use self::index::SourceIndex; +pub use self::source::{SourceFile, SourceId}; +pub use self::span::SourceSpan; + +pub type Diagnostic = codespan_reporting::diagnostic::Diagnostic; + +pub trait ToDiagnostic { + fn to_diagnostic(&self) -> Diagnostic; +} diff --git a/libeir_diagnostics/src/reporting.rs b/libeir_diagnostics/src/reporting.rs deleted file mode 100644 index cef4666..0000000 --- a/libeir_diagnostics/src/reporting.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::cmp::Ordering; -use std::fmt; -use std::str::FromStr; - -pub use termcolor::{Color, ColorChoice, ColorSpec}; - -mod diagnostic; -pub mod emitter; - -pub use self::diagnostic::{Diagnostic, Label, LabelStyle}; -pub use self::emitter::{Emitter, NullEmitter, StandardStreamEmitter}; - -/// A severity level for diagnostic messages -/// -/// These are ordered in the following way: -/// -/// * Bug -/// * Error -/// * Warning -/// * Note -/// * Help -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum Severity { - /// An unexpected bug. - Bug, - /// An error. - Error, - /// A warning. - Warning, - /// A note. - Note, - /// A help message. - Help, -} -impl Severity { - /// We want bugs to be the maximum severity, errors next, etc... - fn to_cmp_int(self) -> u8 { - match self { - Severity::Bug => 5, - Severity::Error => 4, - Severity::Warning => 3, - Severity::Note => 2, - Severity::Help => 1, - } - } -} -impl PartialOrd for Severity { - fn partial_cmp(&self, other: &Severity) -> Option { - u8::partial_cmp(&self.to_cmp_int(), &other.to_cmp_int()) - } -} -impl fmt::Display for Severity { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_str().fmt(f) - } -} -impl Severity { - /// Return the termcolor to use when rendering messages of this diagnostic severity - pub fn color(self) -> Color { - match self { - Severity::Bug | Severity::Error => Color::Red, - Severity::Warning => Color::Yellow, - Severity::Note => Color::Green, - Severity::Help => Color::Cyan, - } - } - - /// A string that explains this diagnostic severity - pub fn to_str(self) -> &'static str { - match self { - Severity::Bug => "error: internal compiler error", - Severity::Error => "error", - Severity::Warning => "warning", - Severity::Note => "note", - Severity::Help => "help", - } - } -} - -/// A command line argument that configures the coloring of the output -/// -/// This can be used with command line argument parsers like `clap` or `structopt`. -/// -/// # Example -/// -/// ```rust -/// use libeir_diagnostics::UseColors; -/// use std::str::FromStr; -/// -/// fn main() { -/// let _color = UseColors::from_str("always"); -/// } -/// ``` -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct UseColors(pub ColorChoice); -impl UseColors { - /// Allowed string variants to be used on the command line - pub const VARIANTS: &'static [&'static str] = &["auto", "always", "ansi", "never"]; -} -impl FromStr for UseColors { - type Err = &'static str; - - fn from_str(src: &str) -> Result { - match src { - _ if src.eq_ignore_ascii_case("auto") => Ok(UseColors(ColorChoice::Auto)), - _ if src.eq_ignore_ascii_case("always") => Ok(UseColors(ColorChoice::Always)), - _ if src.eq_ignore_ascii_case("ansi") => Ok(UseColors(ColorChoice::AlwaysAnsi)), - _ if src.eq_ignore_ascii_case("never") => Ok(UseColors(ColorChoice::Never)), - _ => Err("valid values: auto, always, ansi, never"), - } - } -} -impl Into for UseColors { - fn into(self) -> ColorChoice { - self.0 - } -} diff --git a/libeir_diagnostics/src/reporting/diagnostic.rs b/libeir_diagnostics/src/reporting/diagnostic.rs deleted file mode 100644 index 62668b7..0000000 --- a/libeir_diagnostics/src/reporting/diagnostic.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Diagnostic reporting support for the codespan crate -use std::fmt; - -use crate::diagnostics::ByteSpan; - -use super::Severity; - -/// A style for the label -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum LabelStyle { - /// The main focus of the diagnostic - Primary, - /// Supporting labels that may help to isolate the cause of the diagnostic - Secondary, -} - -/// A label describing an underlined region of code associated with a diagnostic -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Label { - /// The span we are going to include in the final snippet. - pub span: ByteSpan, - /// A message to provide some additional information for the underlined code. - pub message: Option, - /// The style to use for the label. - pub style: LabelStyle, -} -impl Label { - pub fn new(span: ByteSpan, style: LabelStyle) -> Label { - Label { - span, - message: None, - style, - } - } - - pub fn new_primary(span: ByteSpan) -> Label { - Label::new(span, LabelStyle::Primary) - } - - pub fn new_secondary(span: ByteSpan) -> Label { - Label::new(span, LabelStyle::Secondary) - } - - pub fn with_message>(mut self, message: S) -> Label { - self.message = Some(message.into()); - self - } -} - -/// Represents a diagnostic message and associated child messages. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Diagnostic { - /// The overall severity of the diagnostic - pub severity: Severity, - /// An optional code that identifies this diagnostic. - pub code: Option, - /// The main message associated with this diagnostic - pub message: String, - /// The labelled spans marking the regions of code that cause this - /// diagnostic to be raised - pub labels: Vec