diff --git a/lang/src/fs.rs b/lang/src/fs.rs index 85a5cf42..23c50471 100644 --- a/lang/src/fs.rs +++ b/lang/src/fs.rs @@ -75,7 +75,12 @@ impl Options { } } -pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec) -> std::io::Result<()> { +pub fn run_compiler( + root_file: &str, + options: Options, + out: &mut Vec, + warnings: &mut String, +) -> std::io::Result<()> { let parsed = parse_from_fs(options.extra_threads, root_file)?; if (options.fmt || options.fmt_stdout) && !parsed.errors.is_empty() { @@ -101,10 +106,11 @@ pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec) -> std let mut ctx = crate::son::CodegenCtx::default(); *ctx.parser.errors.get_mut() = parsed.errors; let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx); - codegen.push_embeds(parsed.embeds); codegen.generate(ty::Module::MAIN); + *warnings = core::mem::take(&mut *codegen.warnings.borrow_mut()); + if !codegen.errors.borrow().is_empty() { drop(codegen); *out = ctx.parser.errors.into_inner().into_bytes(); diff --git a/lang/src/main.rs b/lang/src/main.rs index 40eb1c83..9dcdbef2 100644 --- a/lang/src/main.rs +++ b/lang/src/main.rs @@ -2,23 +2,28 @@ fn main() { use std::io::Write; - fn run(out: &mut Vec) -> std::io::Result<()> { + fn run(out: &mut Vec, warnings: &mut String) -> std::io::Result<()> { let args = std::env::args().collect::>(); let args = args.iter().map(String::as_str).collect::>(); let opts = hblang::Options::from_args(&args, out)?; let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb"); - hblang::run_compiler(file, opts, out) + hblang::run_compiler(file, opts, out, warnings) } log::set_logger(&hblang::fs::Logger).unwrap(); log::set_max_level(log::LevelFilter::Error); let mut out = Vec::new(); - match run(&mut out) { - Ok(_) => std::io::stdout().write_all(&out).unwrap(), + let mut warnings = String::new(); + match run(&mut out, &mut warnings) { + Ok(_) => { + std::io::stderr().write_all(warnings.as_bytes()).unwrap(); + std::io::stdout().write_all(&out).unwrap() + } Err(_) => { + std::io::stderr().write_all(warnings.as_bytes()).unwrap(); std::io::stderr().write_all(&out).unwrap(); std::process::exit(1); } diff --git a/lang/src/parser.rs b/lang/src/parser.rs index afc4a787..d5c523f1 100644 --- a/lang/src/parser.rs +++ b/lang/src/parser.rs @@ -79,6 +79,7 @@ struct ScopeIdent { ident: Ident, declared: bool, ordered: bool, + used: bool, flags: IdentFlags, } @@ -261,6 +262,7 @@ impl<'a, 'b> Parser<'a, 'b> { self.ctx.idents.push(ScopeIdent { ident, declared: false, + used: false, ordered: false, flags: 0, }); @@ -626,6 +628,8 @@ impl<'a, 'b> Parser<'a, 'b> { if !&self.ctx.idents[i].declared { self.ctx.idents.swap(i, undeclared_count); undeclared_count += 1; + } else if !self.ctx.idents[i].used { + self.warn(self.ctx.idents[i].ident.pos(), "unused identifier"); } } @@ -705,6 +709,19 @@ impl<'a, 'b> Parser<'a, 'b> { } } + #[track_caller] + fn warn(&mut self, pos: Pos, msg: impl fmt::Display) { + if log::log_enabled!(log::Level::Error) { + use core::fmt::Write; + writeln!( + self.ctx.warnings.get_mut(), + "(W) {}", + Report::new(self.lexer.source(), self.path, pos, msg) + ) + .unwrap(); + } + } + #[track_caller] fn report(&mut self, pos: Pos, msg: impl fmt::Display) -> Option { if log::log_enabled!(log::Level::Error) { @@ -1148,6 +1165,7 @@ impl core::fmt::Display for Display<'_> { #[derive(Default)] pub struct Ctx { pub errors: RefCell, + pub warnings: RefCell, symbols: Vec, stack: StackAlloc, idents: Vec, diff --git a/lang/src/son.rs b/lang/src/son.rs index 8f2ccdaa..73529590 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -89,7 +89,7 @@ impl crate::ctx_map::CtxEntry for Nid { macro_rules! inference { ($ty:ident, $ctx:expr, $self:expr, $pos:expr, $subject:literal, $example:literal) => { let Some($ty) = $ctx.ty else { - $self.report( + $self.error( $pos, concat!( "resulting ", @@ -152,7 +152,7 @@ impl Nodes { } depth } - Kind::Start | Kind::End | Kind::Die | Kind::Return => 1, + Kind::Start | Kind::End | Kind::Die | Kind::Return { .. } => 1, u => unreachable!("{u:?}"), }); @@ -1010,7 +1010,7 @@ impl Nodes { return Some(NEVER); } } - K::Return => { + K::Return { file } => { if self[target].inputs[0] == NEVER { return Some(NEVER); } @@ -1065,7 +1065,7 @@ impl Nodes { } if new_inps.as_slice() != self[target].inputs.as_slice() { - return Some(self.new_node_nop(ty::Id::VOID, Kind::Return, new_inps)); + return Some(self.new_node_nop(ty::Id::VOID, Kind::Return { file }, new_inps)); } } K::Phi => { @@ -1544,7 +1544,7 @@ impl Nodes { Kind::End => return Ok(()), Kind::If => write!(out, " if: "), Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"), - Kind::Return => write!(out, " ret: "), + Kind::Return { .. } => write!(out, " ret: "), Kind::Die => write!(out, " die: "), Kind::CInt { value } => write!(out, "cint: #{value:<4}"), Kind::Phi => write!(out, " phi: "), @@ -1642,7 +1642,7 @@ impl Nodes { } node = cfg_index; } - Kind::Return | Kind::Die => { + Kind::Return { .. } | Kind::Die => { node = self[node].outputs[0]; } Kind::Then | Kind::Else | Kind::Entry => { @@ -1847,7 +1847,7 @@ impl Nodes { fn is_data_dep(&self, val: Nid, user: Nid) -> bool { match self[user].kind { - Kind::Return => self[user].inputs[1] == val, + Kind::Return { .. } => self[user].inputs[1] == val, _ if self.is_cfg(user) && !matches!(self[user].kind, Kind::Call { .. } | Kind::If) => { false } @@ -1925,7 +1925,9 @@ pub enum Kind { // [entry, back] Loop, // [ctrl, ?value] - Return, + Return { + file: ty::Module, + }, // [ctrl] Die, // [ctrl] @@ -1986,7 +1988,7 @@ impl Kind { self, Self::Start | Self::End - | Self::Return + | Self::Return { .. } | Self::Die | Self::Entry | Self::Then @@ -1999,7 +2001,7 @@ impl Kind { } fn ends_basic_block(&self) -> bool { - matches!(self, Self::Return | Self::If | Self::End | Self::Die) + matches!(self, Self::Return { .. } | Self::If | Self::End | Self::Die) } fn starts_basic_block(&self) -> bool { @@ -2443,6 +2445,7 @@ impl CodegenCtx { pub struct Codegen<'a> { pub files: &'a [parser::Ast], pub errors: &'a RefCell, + pub warnings: &'a RefCell, tys: &'a mut Types, ci: ItemCtx, pool: &'a mut Pool, @@ -2455,7 +2458,7 @@ impl Drop for Codegen<'_> { fn drop(&mut self) { if debug::panicking() { if let Some(&pos) = self.ci.pos.last() { - self.report(pos, "panic occured here"); + self.error(pos, "panic occured here"); } if !self.errors.borrow().is_empty() { @@ -2474,6 +2477,7 @@ impl<'a> Codegen<'a> { Self { files, errors: &ctx.parser.errors, + warnings: &ctx.parser.warnings, tys: &mut ctx.tys, ci: Default::default(), pool: &mut ctx.pool, @@ -2519,8 +2523,10 @@ impl<'a> Codegen<'a> { } fn emit_and_eval(&mut self, file: Module, ret: ty::Id, ret_loc: &mut [u8]) -> u64 { - let mut rets = - self.ci.nodes[NEVER].inputs.iter().filter(|&&i| self.ci.nodes[i].kind == Kind::Return); + let mut rets = self.ci.nodes[NEVER] + .inputs + .iter() + .filter(|&&i| matches!(self.ci.nodes[i].kind, Kind::Return { .. })); if let Some(&ret) = rets.next() && rets.next().is_none() && let Kind::CInt { value } = self.ci.nodes[self.ci.nodes[ret].inputs[1]].kind @@ -2672,7 +2678,7 @@ impl<'a> Codegen<'a> { inference!(oty, ctx, self, pos, "null pointer", "@as(^, null)"); let Some(ty) = self.tys.inner_of(oty) else { - self.report( + self.error( pos, fa!( "'null' expression was inferred to be '{}', @@ -2739,7 +2745,7 @@ impl<'a> Codegen<'a> { let literal = &literal[1..literal.len() - 1]; let report = |bytes: &core::str::Bytes, message: &str| { - self.report(pos + (literal.len() - bytes.len()) as u32 - 1, message) + self.error(pos + (literal.len() - bytes.len()) as u32 - 1, message) }; let mut data = Vec::::with_capacity(literal.len()); @@ -2786,7 +2792,11 @@ impl<'a> Codegen<'a> { } } - let ret = self.ci.nodes.new_node_nop(ty::Id::VOID, Kind::Return, inps); + let ret = self.ci.nodes.new_node_nop( + ty::Id::VOID, + Kind::Return { file: self.ci.file }, + inps, + ); self.ci.ctrl.set(NEVER, &mut self.ci.nodes); self.ci.nodes[ret].pos = pos; self.ci.nodes.bind(ret, NEVER); @@ -2887,7 +2897,7 @@ impl<'a> Codegen<'a> { .map(|f| self.tys.names.ident_str(f.name)) .intersperse("', '") .collect::(); - self.report( + self.error( pos, fa!( "the '{}' does not have this field, \ @@ -2901,7 +2911,7 @@ impl<'a> Codegen<'a> { Some(Value::ptr(self.offset(vtarget.id, offset)).ty(ty)) } _ => { - self.report( + self.error( pos, fa!( "the '{}' is not a struct, or pointer to one, or enum, \ @@ -2937,7 +2947,7 @@ impl<'a> Codegen<'a> { self.implicit_unwrap(val.pos(), &mut vl); let Some(base) = self.tys.base_of(vl.ty) else { - self.report( + self.error( pos, fa!("the '{}' can not be dereferneced", self.ty_display(vl.ty)), ); @@ -2951,7 +2961,7 @@ impl<'a> Codegen<'a> { inference!(ty, ctx, self, pos, "enum type", ".Variant"); let ty::Kind::Enum(e) = ty.expand() else { - self.report( + self.error( pos, fa!("expected inferred type to be enum but got '{}'", self.ty_display(ty)), ); @@ -2980,7 +2990,7 @@ impl<'a> Codegen<'a> { self.tys, )) } else { - self.report(pos, fa!("cant negate '{}'", self.ty_display(val.ty))); + self.error(pos, fa!("cant negate '{}'", self.ty_display(val.ty))); Value::NEVER } } @@ -2995,7 +3005,7 @@ impl<'a> Codegen<'a> { self.tys, )) } else { - self.report(pos, fa!("cant logically negate '{}'", self.ty_display(val.ty))); + self.error(pos, fa!("cant logically negate '{}'", self.ty_display(val.ty))); Value::NEVER } } @@ -3034,13 +3044,13 @@ impl<'a> Codegen<'a> { } else if dest.ptr { self.store_mem(dest.id, dest.ty, value.id); } else { - self.report(pos, "cannot assign to this expression"); + self.error(pos, "cannot assign to this expression"); } Some(Value::VOID) } Expr::BinOp { left: &Expr::Null { pos }, .. } => { - self.report(pos, "'null' must always be no the right side of an expression"); + self.error(pos, "'null' must always be no the right side of an expression"); Value::NEVER } Expr::BinOp { @@ -3053,7 +3063,7 @@ impl<'a> Codegen<'a> { self.strip_var(&mut cmped); let Some(ty) = self.tys.inner_of(cmped.ty) else { - self.report( + self.error( left.pos(), fa!("'{}' is never null, remove this check", self.ty_display(cmped.ty)), ); @@ -3135,7 +3145,7 @@ impl<'a> Codegen<'a> { .or(Value::NEVER) } _ => { - self.report( + self.error( pos, fa!("'{} {op} _' is not supported", self.ty_display(lhs.ty)), ); @@ -3153,7 +3163,7 @@ impl<'a> Codegen<'a> { } let ty::Kind::Slice(s) = bs.ty.expand() else { - self.report( + self.error( base.pos(), fa!( "cant index into '{}' which is not array nor slice", @@ -3209,7 +3219,7 @@ impl<'a> Codegen<'a> { let (got, expected) = (self.tys.size_of(val.ty), self.tys.size_of(ty)); if got != expected { - self.report( + self.error( pos, fa!( "cast from '{}' to '{}' is not supported, \ @@ -3239,7 +3249,7 @@ impl<'a> Codegen<'a> { self.strip_var(&mut val); if !val.ty.is_optional() { - self.report( + self.error( expr.pos(), fa!( "only optional types can be unwrapped ('{}' is not optional)", @@ -3256,7 +3266,7 @@ impl<'a> Codegen<'a> { let mut val = self.expr(expr)?; if !val.ty.is_integer() { - self.report( + self.error( expr.pos(), fa!( "only integers can be truncated ('{}' is not an integer)", @@ -3269,7 +3279,7 @@ impl<'a> Codegen<'a> { inference!(ty, ctx, self, pos, "integer", "@as(, @intcast())"); if !ty.is_integer() { - self.report( + self.error( expr.pos(), fa!( "intcast is inferred to output '{}', which is not an integer", @@ -3289,7 +3299,7 @@ impl<'a> Codegen<'a> { let val = self.expr(expr)?; if !val.ty.is_float() { - self.report( + self.error( expr.pos(), fa!( "only floats can be truncated ('{}' is not a float)", @@ -3302,7 +3312,7 @@ impl<'a> Codegen<'a> { inference!(ty, ctx, self, pos, "float", "@as(, @floatcast())"); if !ty.is_float() { - self.report( + self.error( expr.pos(), fa!( "floatcast is inferred to output '{}', which is not a float", @@ -3328,7 +3338,7 @@ impl<'a> Codegen<'a> { let ret_ty = match val.ty { ty::Id::F32 | ty::Id::F64 => ty::Id::INT, _ => { - self.report( + self.error( expr.pos(), fa!("expected float ('{}' is not a float)", self.ty_display(val.ty)), ); @@ -3368,7 +3378,7 @@ impl<'a> Codegen<'a> { && ity.try_upcast(ty) == Some(ty) && val.ty == ity { - self.report(pos, "the type is known at this point, remove the hint"); + self.error(pos, "the type is known at this point, remove the hint"); } self.strip_var(&mut val); self.assert_ty(expr.pos(), &mut val, ty, "hinted expr"); @@ -3432,7 +3442,7 @@ impl<'a> Codegen<'a> { let mut offs = OffsetIter::new(s, self.tys); for field in fields { let Some((ty, offset)) = offs.next_ty(self.tys) else { - self.report( + self.error( field.pos(), "this init argumen overflows the field count", ); @@ -3453,7 +3463,7 @@ impl<'a> Codegen<'a> { .collect::(); if !field_list.is_empty() { - self.report( + self.error( pos, fa!("the struct initializer is missing {field_list} \ (append them to the end of the constructor)"), @@ -3471,7 +3481,7 @@ impl<'a> Codegen<'a> { .map_or_else(|| self.tys.make_array(elem, len as ArrayLen), |_| sty); if len != fields.len() { - self.report( + self.error( pos, fa!( "expected '{}' but constructor has {} elements", @@ -3497,7 +3507,7 @@ impl<'a> Codegen<'a> { } _ => { let inferred = if ty.is_some() { "" } else { "inferred " }; - self.report( + self.error( pos, fa!( "the {inferred}type of the constructor is `{}`, \ @@ -3521,7 +3531,7 @@ impl<'a> Codegen<'a> { let ty::Kind::Struct(s) = sty.expand() else { let inferred = if ty.is_some() { "" } else { "inferred " }; - self.report( + self.error( pos, fa!( "the {inferred}type of the constructor is `{}`, \ @@ -3540,7 +3550,7 @@ impl<'a> Codegen<'a> { let mem = self.new_stack(pos, sty); for field in fields { let Some(index) = self.tys.find_struct_field(s, field.name) else { - self.report( + self.error( field.pos, fa!("struct '{}' does not have this field", self.ty_display(sty)), ); @@ -3551,8 +3561,8 @@ impl<'a> Codegen<'a> { mem::replace(&mut offs[index], (ty::Id::UNDECLARED, field.pos)); if ty == ty::Id::UNDECLARED { - self.report(field.pos, "the struct field is already initialized"); - self.report(offset, "previous initialization is here"); + self.error(field.pos, "the struct field is already initialized"); + self.error(offset, "previous initialization is here"); continue; } @@ -3573,7 +3583,7 @@ impl<'a> Codegen<'a> { .collect::(); if !field_list.is_empty() { - self.report(pos, fa!("the struct initializer is missing {field_list}")); + self.error(pos, fa!("the struct initializer is missing {field_list}")); } Some(Value::ptr(mem).ty(sty)) @@ -3587,7 +3597,7 @@ impl<'a> Codegen<'a> { ret = ret.and(self.expr(stmt)); if let Some(mut id) = ret { if id.ty != ty::Id::VOID { - self.report( + self.error( stmt.pos(), fa!( "statements need to evaluate to 'void', \ @@ -3890,7 +3900,7 @@ impl<'a> Codegen<'a> { let value = self.expr(value)?; let ty::Kind::Enum(e) = value.ty.expand() else { - self.report( + self.error( pos, fa!( "match operates on enums (for now), '{}' is not an enum", @@ -3906,8 +3916,8 @@ impl<'a> Codegen<'a> { for &MatchBranch { pat, pos: bp, body } in branches { if let Expr::Wildcard { .. } = pat { if let Some(prev) = else_branch { - self.report(bp, "duplicate branch"); - self.report(prev.pos(), "...first branch declared here"); + self.error(bp, "duplicate branch"); + self.error(prev.pos(), "...first branch declared here"); } else_branch = Some(body); @@ -3916,8 +3926,8 @@ impl<'a> Codegen<'a> { let pat_val = self.eval_const(self.ci.file, &pat, value.ty); if covered_values[pat_val as usize] != Pos::MAX { - self.report(bp, "duplicate branch"); - self.report( + self.error(bp, "duplicate branch"); + self.error( covered_values[pat_val as usize], "...first branch declared here", ); @@ -3966,10 +3976,7 @@ impl<'a> Codegen<'a> { .collect::(); if !missing_branches.is_empty() { - self.report( - pos, - fa!("not all cases covered, missing '{missing_branches}'"), - ); + self.error(pos, fa!("not all cases covered, missing '{missing_branches}'")); } self.ci.ctrl.get() }; @@ -3989,7 +3996,7 @@ impl<'a> Codegen<'a> { Some(Value::VOID) } ref e => { - self.report_unhandled_ast(e, "bruh"); + self.error_unhandled_ast(e, "bruh"); Value::NEVER } } @@ -4035,7 +4042,7 @@ impl<'a> Codegen<'a> { .map(|f| self.tys.names.ident_str(f.name)) .intersperse("', '") .collect::(); - self.report( + self.error( pos, fa!( "the '{}' does not have this variant, \ @@ -4070,7 +4077,7 @@ impl<'a> Codegen<'a> { fn gen_call(&mut self, func: &Expr, args: &[Expr], inline: bool) -> Option { let ty = self.ty(func); let ty::Kind::Func(mut fu) = ty.expand() else { - self.report(func.pos(), fa!("compiler cant (yet) call '{}'", self.ty_display(ty))); + self.error(func.pos(), fa!("compiler cant (yet) call '{}'", self.ty_display(ty))); return Value::NEVER; }; @@ -4083,7 +4090,7 @@ impl<'a> Codegen<'a> { let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else { unreachable!() }; if args.len() != cargs.len() { - self.report( + self.error( func.pos(), fa!( "expected {} function argumenr{}, got {}", @@ -4095,7 +4102,7 @@ impl<'a> Codegen<'a> { } if inline && is_inline { - self.report( + self.error( func.pos(), "function is declared as inline so this @inline directive only reduces readability", ); @@ -4133,7 +4140,7 @@ impl<'a> Codegen<'a> { if sig.ret == ty::Id::VOID { self.expr(&Expr::Return { pos: body.pos(), val: None }); } else { - self.report( + self.error( body.pos(), "expected all paths in the fucntion to return \ or the return type to be 'void'", @@ -4160,7 +4167,7 @@ impl<'a> Codegen<'a> { && (!self.ci.nodes[ctrl.get()].kind.is_eca() || self.ci.nodes[ctrl.get()].inputs[0] != prev_ctrl) { - self.report(body.pos(), "function is makred inline but it contains controlflow"); + self.error(body.pos(), "function is makred inline but it contains controlflow"); } scope.vars.drain(var_base..).for_each(|v| v.remove(&mut self.ci.nodes)); @@ -4301,13 +4308,13 @@ impl<'a> Codegen<'a> { } ty::Kind::Struct(is) => { if !self.struct_op(pos, op, is, dst, lhs, rhs) { - self.report( + self.error( pos, fa!("... when appliing '{0} {op} {0}'", self.ty_display(s.into())), ); } } - _ => self.report(pos, fa!("'{0} {op} {0}' is not supported", self.ty_display(ty))), + _ => self.error(pos, fa!("'{0} {op} {0}' is not supported", self.ty_display(ty))), } } @@ -4344,7 +4351,7 @@ impl<'a> Codegen<'a> { ty::Kind::Struct(is) => match self.struct_fold_op(pos, op, fold_op, is, lhs, rhs) { Some(v) => v.id, None => { - self.report( + self.error( pos, fa!("...when appliing '{0} {op} {0}'", self.ty_display(s.into())), ); @@ -4352,7 +4359,7 @@ impl<'a> Codegen<'a> { } }, _ => { - self.report(pos, fa!("'{0} {op} {0}' is not supported", self.ty_display(ty))); + self.error(pos, fa!("'{0} {op} {0}' is not supported", self.ty_display(ty))); return None; } }; @@ -4398,7 +4405,7 @@ impl<'a> Codegen<'a> { ty::Id::UNDECLARED } else { if ty != ty::Id::TYPE { - self.report( + self.error( arg.pos(), fa!( "arbitrary comptime types are not supported yet \ @@ -4424,7 +4431,7 @@ impl<'a> Codegen<'a> { } let Some(args) = self.tys.pack_args(arg_base) else { - self.report(pos, "function instance has too many arguments"); + self.error(pos, "function instance has too many arguments"); return None; }; let ret = self.ty_in(file, ret); @@ -4463,20 +4470,20 @@ impl<'a> Codegen<'a> { } Expr::Ctor { pos, fields, .. } => { let ty::Kind::Struct(idx) = right.ty.expand() else { - self.report(pos, "can't use struct destruct on non struct value (TODO: shold work with modules)"); + self.error(pos, "can't use struct destruct on non struct value (TODO: shold work with modules)"); return; }; for &CtorField { pos, name, ref value } in fields { let Some((offset, ty)) = OffsetIter::offset_of(self.tys, idx, name) else { - self.report(pos, format_args!("field not found: {name:?}")); + self.error(pos, format_args!("field not found: {name:?}")); continue; }; let off = self.offset(right.id, offset); self.assign_pattern(value, Value::ptr(off).ty(ty)); } } - ref pat => self.report_unhandled_ast(pat, "pattern"), + ref pat => self.error_unhandled_ast(pat, "pattern"), } } @@ -4521,7 +4528,7 @@ impl<'a> Codegen<'a> { fn jump_to(&mut self, pos: Pos, id: usize) -> Option { let Some(mut loob) = self.ci.loops.last_mut() else { - self.report(pos, "break outside a loop"); + self.error(pos, "break outside a loop"); return None; }; @@ -4639,7 +4646,7 @@ impl<'a> Codegen<'a> { if sig.ret == ty::Id::VOID { self.expr(&Expr::Return { pos: body.pos(), val: None }); } else { - self.report( + self.error( body.pos(), fa!( "expected all paths in the fucntion to return \ @@ -4693,23 +4700,25 @@ impl<'a> Codegen<'a> { } _ => unreachable!(), }; - self.report(pos, msg); + self.error(pos, msg); } for &node in self.ci.nodes[NEVER].inputs.iter() { - if self.ci.nodes[node].kind == Kind::Return + if let Kind::Return { file } = self.ci.nodes[node].kind && self.ci.nodes[self.ci.nodes.aclass_index(self.ci.nodes[node].inputs[1]).1].kind == Kind::Stck { - self.report( + let pfile = mem::replace(&mut self.ci.file, file); + self.error( self.ci.nodes[node].pos, "returning value with local provenance \ (pointer will be invalid after function returns)", ); - self.report( + self.error( self.ci.nodes[self.ci.nodes.aclass_index(self.ci.nodes[node].inputs[1]).1].pos, "...the pointer points to stack allocation created here", ); + self.ci.file = pfile; } } @@ -4783,7 +4792,7 @@ impl<'a> Codegen<'a> { } else { let ty = self.ty_display(lhs.ty); let expected = self.ty_display(rhs.ty); - self.report(pos, fa!("'{ty} {op} {expected}' is not supported")); + self.error(pos, fa!("'{ty} {op} {expected}' is not supported")); (ty::Id::NEVER, VOID) } } @@ -4945,7 +4954,7 @@ impl<'a> Codegen<'a> { let ty = self.ty_display(src.ty); let expected = self.ty_display(expected); - self.report(pos, fa!("expected {hint} to be of type {expected}, got {ty}")); + self.error(pos, fa!("expected {hint} to be of type {expected}, got {ty}")); false } } @@ -4958,16 +4967,22 @@ impl<'a> Codegen<'a> { value.ty = to; } + //#[track_caller] + //fn warn(&self, pos: Pos, msg: impl core::fmt::Display) { + // let mut buf = self.warnings.borrow_mut(); + // write!(buf, "{}", self.file().report(pos, msg)).unwrap(); + //} + #[track_caller] - fn report(&self, pos: Pos, msg: impl core::fmt::Display) { + fn error(&self, pos: Pos, msg: impl core::fmt::Display) { let mut buf = self.errors.borrow_mut(); write!(buf, "{}", self.file().report(pos, msg)).unwrap(); } #[track_caller] - fn report_unhandled_ast(&self, ast: &Expr, hint: impl Display) { + fn error_unhandled_ast(&self, ast: &Expr, hint: impl Display) { log::info!("{ast:#?}"); - self.report(ast.pos(), fa!("compiler does not (yet) know how to handle ({hint})")); + self.error(ast.pos(), fa!("compiler does not (yet) know how to handle ({hint})")); } fn file(&self) -> &'a parser::Ast { diff --git a/lang/src/son/hbvm.rs b/lang/src/son/hbvm.rs index 3e2c6b63..c8f10734 100644 --- a/lang/src/son/hbvm.rs +++ b/lang/src/son/hbvm.rs @@ -238,7 +238,7 @@ impl Backend for HbvmBackend { debug_assert_matches!( nodes[stck].kind, Kind::Phi - | Kind::Return + | Kind::Return { .. } | Kind::Load | Kind::Call { .. } | Kind::Stre @@ -524,7 +524,7 @@ impl HbvmBackend { self.emit(instrs::jmp(0)); } } - Kind::Return => { + Kind::Return { .. } => { match retl { Some(PLoc::Reg(r, size)) if sig.ret.loc(tys) == Loc::Stack => { self.emit(instrs::ld(r, allocs[0], 0, size)) diff --git a/lang/src/son/hbvm/regalloc.rs b/lang/src/son/hbvm/regalloc.rs index f6c309a9..c5fcebce 100644 --- a/lang/src/son/hbvm/regalloc.rs +++ b/lang/src/son/hbvm/regalloc.rs @@ -183,7 +183,7 @@ impl HbvmBackend { } is_next_block = res.backrefs[nid as usize] as usize == i + 1; } - Kind::Return => { + Kind::Return { .. } => { let &[_, ret, ..] = node.inputs.as_slice() else { unreachable!() }; match retl { Some(PLoc::Reg(r, _)) if sig.ret.loc(tys) == Loc::Reg => { @@ -423,7 +423,7 @@ impl<'a> Function<'a> { self.emit_node(o); } } - Kind::Return | Kind::Die => { + Kind::Return { .. } | Kind::Die => { self.close_block(nid); self.emit_node(node.outputs[0]); }