fixing the false return location

This commit is contained in:
Jakub Doka 2024-11-17 18:15:58 +01:00
parent a7718e1220
commit 8892dd729a
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
6 changed files with 135 additions and 91 deletions

View file

@ -75,7 +75,12 @@ impl Options {
} }
} }
pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec<u8>) -> std::io::Result<()> { pub fn run_compiler(
root_file: &str,
options: Options,
out: &mut Vec<u8>,
warnings: &mut String,
) -> std::io::Result<()> {
let parsed = parse_from_fs(options.extra_threads, root_file)?; let parsed = parse_from_fs(options.extra_threads, root_file)?;
if (options.fmt || options.fmt_stdout) && !parsed.errors.is_empty() { 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<u8>) -> std
let mut ctx = crate::son::CodegenCtx::default(); let mut ctx = crate::son::CodegenCtx::default();
*ctx.parser.errors.get_mut() = parsed.errors; *ctx.parser.errors.get_mut() = parsed.errors;
let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx); let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx);
codegen.push_embeds(parsed.embeds); codegen.push_embeds(parsed.embeds);
codegen.generate(ty::Module::MAIN); codegen.generate(ty::Module::MAIN);
*warnings = core::mem::take(&mut *codegen.warnings.borrow_mut());
if !codegen.errors.borrow().is_empty() { if !codegen.errors.borrow().is_empty() {
drop(codegen); drop(codegen);
*out = ctx.parser.errors.into_inner().into_bytes(); *out = ctx.parser.errors.into_inner().into_bytes();

View file

@ -2,23 +2,28 @@
fn main() { fn main() {
use std::io::Write; use std::io::Write;
fn run(out: &mut Vec<u8>) -> std::io::Result<()> { fn run(out: &mut Vec<u8>, warnings: &mut String) -> std::io::Result<()> {
let args = std::env::args().collect::<Vec<_>>(); let args = std::env::args().collect::<Vec<_>>();
let args = args.iter().map(String::as_str).collect::<Vec<_>>(); let args = args.iter().map(String::as_str).collect::<Vec<_>>();
let opts = hblang::Options::from_args(&args, out)?; let opts = hblang::Options::from_args(&args, out)?;
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb"); 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_logger(&hblang::fs::Logger).unwrap();
log::set_max_level(log::LevelFilter::Error); log::set_max_level(log::LevelFilter::Error);
let mut out = Vec::new(); let mut out = Vec::new();
match run(&mut out) { let mut warnings = String::new();
Ok(_) => std::io::stdout().write_all(&out).unwrap(), match run(&mut out, &mut warnings) {
Ok(_) => {
std::io::stderr().write_all(warnings.as_bytes()).unwrap();
std::io::stdout().write_all(&out).unwrap()
}
Err(_) => { Err(_) => {
std::io::stderr().write_all(warnings.as_bytes()).unwrap();
std::io::stderr().write_all(&out).unwrap(); std::io::stderr().write_all(&out).unwrap();
std::process::exit(1); std::process::exit(1);
} }

View file

@ -79,6 +79,7 @@ struct ScopeIdent {
ident: Ident, ident: Ident,
declared: bool, declared: bool,
ordered: bool, ordered: bool,
used: bool,
flags: IdentFlags, flags: IdentFlags,
} }
@ -261,6 +262,7 @@ impl<'a, 'b> Parser<'a, 'b> {
self.ctx.idents.push(ScopeIdent { self.ctx.idents.push(ScopeIdent {
ident, ident,
declared: false, declared: false,
used: false,
ordered: false, ordered: false,
flags: 0, flags: 0,
}); });
@ -626,6 +628,8 @@ impl<'a, 'b> Parser<'a, 'b> {
if !&self.ctx.idents[i].declared { if !&self.ctx.idents[i].declared {
self.ctx.idents.swap(i, undeclared_count); self.ctx.idents.swap(i, undeclared_count);
undeclared_count += 1; 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] #[track_caller]
fn report(&mut self, pos: Pos, msg: impl fmt::Display) -> Option<!> { fn report(&mut self, pos: Pos, msg: impl fmt::Display) -> Option<!> {
if log::log_enabled!(log::Level::Error) { if log::log_enabled!(log::Level::Error) {
@ -1148,6 +1165,7 @@ impl core::fmt::Display for Display<'_> {
#[derive(Default)] #[derive(Default)]
pub struct Ctx { pub struct Ctx {
pub errors: RefCell<String>, pub errors: RefCell<String>,
pub warnings: RefCell<String>,
symbols: Vec<Symbol>, symbols: Vec<Symbol>,
stack: StackAlloc, stack: StackAlloc,
idents: Vec<ScopeIdent>, idents: Vec<ScopeIdent>,

View file

@ -89,7 +89,7 @@ impl crate::ctx_map::CtxEntry for Nid {
macro_rules! inference { macro_rules! inference {
($ty:ident, $ctx:expr, $self:expr, $pos:expr, $subject:literal, $example:literal) => { ($ty:ident, $ctx:expr, $self:expr, $pos:expr, $subject:literal, $example:literal) => {
let Some($ty) = $ctx.ty else { let Some($ty) = $ctx.ty else {
$self.report( $self.error(
$pos, $pos,
concat!( concat!(
"resulting ", "resulting ",
@ -152,7 +152,7 @@ impl Nodes {
} }
depth depth
} }
Kind::Start | Kind::End | Kind::Die | Kind::Return => 1, Kind::Start | Kind::End | Kind::Die | Kind::Return { .. } => 1,
u => unreachable!("{u:?}"), u => unreachable!("{u:?}"),
}); });
@ -1010,7 +1010,7 @@ impl Nodes {
return Some(NEVER); return Some(NEVER);
} }
} }
K::Return => { K::Return { file } => {
if self[target].inputs[0] == NEVER { if self[target].inputs[0] == NEVER {
return Some(NEVER); return Some(NEVER);
} }
@ -1065,7 +1065,7 @@ impl Nodes {
} }
if new_inps.as_slice() != self[target].inputs.as_slice() { 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 => { K::Phi => {
@ -1544,7 +1544,7 @@ impl Nodes {
Kind::End => return Ok(()), Kind::End => return Ok(()),
Kind::If => write!(out, " if: "), Kind::If => write!(out, " if: "),
Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"), Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"),
Kind::Return => write!(out, " ret: "), Kind::Return { .. } => write!(out, " ret: "),
Kind::Die => write!(out, " die: "), Kind::Die => write!(out, " die: "),
Kind::CInt { value } => write!(out, "cint: #{value:<4}"), Kind::CInt { value } => write!(out, "cint: #{value:<4}"),
Kind::Phi => write!(out, " phi: "), Kind::Phi => write!(out, " phi: "),
@ -1642,7 +1642,7 @@ impl Nodes {
} }
node = cfg_index; node = cfg_index;
} }
Kind::Return | Kind::Die => { Kind::Return { .. } | Kind::Die => {
node = self[node].outputs[0]; node = self[node].outputs[0];
} }
Kind::Then | Kind::Else | Kind::Entry => { Kind::Then | Kind::Else | Kind::Entry => {
@ -1847,7 +1847,7 @@ impl Nodes {
fn is_data_dep(&self, val: Nid, user: Nid) -> bool { fn is_data_dep(&self, val: Nid, user: Nid) -> bool {
match self[user].kind { 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) => { _ if self.is_cfg(user) && !matches!(self[user].kind, Kind::Call { .. } | Kind::If) => {
false false
} }
@ -1925,7 +1925,9 @@ pub enum Kind {
// [entry, back] // [entry, back]
Loop, Loop,
// [ctrl, ?value] // [ctrl, ?value]
Return, Return {
file: ty::Module,
},
// [ctrl] // [ctrl]
Die, Die,
// [ctrl] // [ctrl]
@ -1986,7 +1988,7 @@ impl Kind {
self, self,
Self::Start Self::Start
| Self::End | Self::End
| Self::Return | Self::Return { .. }
| Self::Die | Self::Die
| Self::Entry | Self::Entry
| Self::Then | Self::Then
@ -1999,7 +2001,7 @@ impl Kind {
} }
fn ends_basic_block(&self) -> bool { 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 { fn starts_basic_block(&self) -> bool {
@ -2443,6 +2445,7 @@ impl CodegenCtx {
pub struct Codegen<'a> { pub struct Codegen<'a> {
pub files: &'a [parser::Ast], pub files: &'a [parser::Ast],
pub errors: &'a RefCell<String>, pub errors: &'a RefCell<String>,
pub warnings: &'a RefCell<String>,
tys: &'a mut Types, tys: &'a mut Types,
ci: ItemCtx, ci: ItemCtx,
pool: &'a mut Pool, pool: &'a mut Pool,
@ -2455,7 +2458,7 @@ impl Drop for Codegen<'_> {
fn drop(&mut self) { fn drop(&mut self) {
if debug::panicking() { if debug::panicking() {
if let Some(&pos) = self.ci.pos.last() { 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() { if !self.errors.borrow().is_empty() {
@ -2474,6 +2477,7 @@ impl<'a> Codegen<'a> {
Self { Self {
files, files,
errors: &ctx.parser.errors, errors: &ctx.parser.errors,
warnings: &ctx.parser.warnings,
tys: &mut ctx.tys, tys: &mut ctx.tys,
ci: Default::default(), ci: Default::default(),
pool: &mut ctx.pool, 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 { fn emit_and_eval(&mut self, file: Module, ret: ty::Id, ret_loc: &mut [u8]) -> u64 {
let mut rets = let mut rets = self.ci.nodes[NEVER]
self.ci.nodes[NEVER].inputs.iter().filter(|&&i| self.ci.nodes[i].kind == Kind::Return); .inputs
.iter()
.filter(|&&i| matches!(self.ci.nodes[i].kind, Kind::Return { .. }));
if let Some(&ret) = rets.next() if let Some(&ret) = rets.next()
&& rets.next().is_none() && rets.next().is_none()
&& let Kind::CInt { value } = self.ci.nodes[self.ci.nodes[ret].inputs[1]].kind && 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(^<ty>, null)"); inference!(oty, ctx, self, pos, "null pointer", "@as(^<ty>, null)");
let Some(ty) = self.tys.inner_of(oty) else { let Some(ty) = self.tys.inner_of(oty) else {
self.report( self.error(
pos, pos,
fa!( fa!(
"'null' expression was inferred to be '{}', "'null' expression was inferred to be '{}',
@ -2739,7 +2745,7 @@ impl<'a> Codegen<'a> {
let literal = &literal[1..literal.len() - 1]; let literal = &literal[1..literal.len() - 1];
let report = |bytes: &core::str::Bytes, message: &str| { 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::<u8>::with_capacity(literal.len()); let mut data = Vec::<u8>::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.ctrl.set(NEVER, &mut self.ci.nodes);
self.ci.nodes[ret].pos = pos; self.ci.nodes[ret].pos = pos;
self.ci.nodes.bind(ret, NEVER); self.ci.nodes.bind(ret, NEVER);
@ -2887,7 +2897,7 @@ impl<'a> Codegen<'a> {
.map(|f| self.tys.names.ident_str(f.name)) .map(|f| self.tys.names.ident_str(f.name))
.intersperse("', '") .intersperse("', '")
.collect::<String>(); .collect::<String>();
self.report( self.error(
pos, pos,
fa!( fa!(
"the '{}' does not have this field, \ "the '{}' does not have this field, \
@ -2901,7 +2911,7 @@ impl<'a> Codegen<'a> {
Some(Value::ptr(self.offset(vtarget.id, offset)).ty(ty)) Some(Value::ptr(self.offset(vtarget.id, offset)).ty(ty))
} }
_ => { _ => {
self.report( self.error(
pos, pos,
fa!( fa!(
"the '{}' is not a struct, or pointer to one, or enum, \ "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); self.implicit_unwrap(val.pos(), &mut vl);
let Some(base) = self.tys.base_of(vl.ty) else { let Some(base) = self.tys.base_of(vl.ty) else {
self.report( self.error(
pos, pos,
fa!("the '{}' can not be dereferneced", self.ty_display(vl.ty)), 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", "<EnumTy>.Variant"); inference!(ty, ctx, self, pos, "enum type", "<EnumTy>.Variant");
let ty::Kind::Enum(e) = ty.expand() else { let ty::Kind::Enum(e) = ty.expand() else {
self.report( self.error(
pos, pos,
fa!("expected inferred type to be enum but got '{}'", self.ty_display(ty)), fa!("expected inferred type to be enum but got '{}'", self.ty_display(ty)),
); );
@ -2980,7 +2990,7 @@ impl<'a> Codegen<'a> {
self.tys, self.tys,
)) ))
} else { } 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 Value::NEVER
} }
} }
@ -2995,7 +3005,7 @@ impl<'a> Codegen<'a> {
self.tys, self.tys,
)) ))
} else { } 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 Value::NEVER
} }
} }
@ -3034,13 +3044,13 @@ impl<'a> Codegen<'a> {
} else if dest.ptr { } else if dest.ptr {
self.store_mem(dest.id, dest.ty, value.id); self.store_mem(dest.id, dest.ty, value.id);
} else { } else {
self.report(pos, "cannot assign to this expression"); self.error(pos, "cannot assign to this expression");
} }
Some(Value::VOID) Some(Value::VOID)
} }
Expr::BinOp { left: &Expr::Null { pos }, .. } => { 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 Value::NEVER
} }
Expr::BinOp { Expr::BinOp {
@ -3053,7 +3063,7 @@ impl<'a> Codegen<'a> {
self.strip_var(&mut cmped); self.strip_var(&mut cmped);
let Some(ty) = self.tys.inner_of(cmped.ty) else { let Some(ty) = self.tys.inner_of(cmped.ty) else {
self.report( self.error(
left.pos(), left.pos(),
fa!("'{}' is never null, remove this check", self.ty_display(cmped.ty)), fa!("'{}' is never null, remove this check", self.ty_display(cmped.ty)),
); );
@ -3135,7 +3145,7 @@ impl<'a> Codegen<'a> {
.or(Value::NEVER) .or(Value::NEVER)
} }
_ => { _ => {
self.report( self.error(
pos, pos,
fa!("'{} {op} _' is not supported", self.ty_display(lhs.ty)), 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 { let ty::Kind::Slice(s) = bs.ty.expand() else {
self.report( self.error(
base.pos(), base.pos(),
fa!( fa!(
"cant index into '{}' which is not array nor slice", "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)); let (got, expected) = (self.tys.size_of(val.ty), self.tys.size_of(ty));
if got != expected { if got != expected {
self.report( self.error(
pos, pos,
fa!( fa!(
"cast from '{}' to '{}' is not supported, \ "cast from '{}' to '{}' is not supported, \
@ -3239,7 +3249,7 @@ impl<'a> Codegen<'a> {
self.strip_var(&mut val); self.strip_var(&mut val);
if !val.ty.is_optional() { if !val.ty.is_optional() {
self.report( self.error(
expr.pos(), expr.pos(),
fa!( fa!(
"only optional types can be unwrapped ('{}' is not optional)", "only optional types can be unwrapped ('{}' is not optional)",
@ -3256,7 +3266,7 @@ impl<'a> Codegen<'a> {
let mut val = self.expr(expr)?; let mut val = self.expr(expr)?;
if !val.ty.is_integer() { if !val.ty.is_integer() {
self.report( self.error(
expr.pos(), expr.pos(),
fa!( fa!(
"only integers can be truncated ('{}' is not an integer)", "only integers can be truncated ('{}' is not an integer)",
@ -3269,7 +3279,7 @@ impl<'a> Codegen<'a> {
inference!(ty, ctx, self, pos, "integer", "@as(<ty>, @intcast(<expr>))"); inference!(ty, ctx, self, pos, "integer", "@as(<ty>, @intcast(<expr>))");
if !ty.is_integer() { if !ty.is_integer() {
self.report( self.error(
expr.pos(), expr.pos(),
fa!( fa!(
"intcast is inferred to output '{}', which is not an integer", "intcast is inferred to output '{}', which is not an integer",
@ -3289,7 +3299,7 @@ impl<'a> Codegen<'a> {
let val = self.expr(expr)?; let val = self.expr(expr)?;
if !val.ty.is_float() { if !val.ty.is_float() {
self.report( self.error(
expr.pos(), expr.pos(),
fa!( fa!(
"only floats can be truncated ('{}' is not a float)", "only floats can be truncated ('{}' is not a float)",
@ -3302,7 +3312,7 @@ impl<'a> Codegen<'a> {
inference!(ty, ctx, self, pos, "float", "@as(<floaty>, @floatcast(<expr>))"); inference!(ty, ctx, self, pos, "float", "@as(<floaty>, @floatcast(<expr>))");
if !ty.is_float() { if !ty.is_float() {
self.report( self.error(
expr.pos(), expr.pos(),
fa!( fa!(
"floatcast is inferred to output '{}', which is not a float", "floatcast is inferred to output '{}', which is not a float",
@ -3328,7 +3338,7 @@ impl<'a> Codegen<'a> {
let ret_ty = match val.ty { let ret_ty = match val.ty {
ty::Id::F32 | ty::Id::F64 => ty::Id::INT, ty::Id::F32 | ty::Id::F64 => ty::Id::INT,
_ => { _ => {
self.report( self.error(
expr.pos(), expr.pos(),
fa!("expected float ('{}' is not a float)", self.ty_display(val.ty)), 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) && ity.try_upcast(ty) == Some(ty)
&& val.ty == ity && 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.strip_var(&mut val);
self.assert_ty(expr.pos(), &mut val, ty, "hinted expr"); 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); let mut offs = OffsetIter::new(s, self.tys);
for field in fields { for field in fields {
let Some((ty, offset)) = offs.next_ty(self.tys) else { let Some((ty, offset)) = offs.next_ty(self.tys) else {
self.report( self.error(
field.pos(), field.pos(),
"this init argumen overflows the field count", "this init argumen overflows the field count",
); );
@ -3453,7 +3463,7 @@ impl<'a> Codegen<'a> {
.collect::<String>(); .collect::<String>();
if !field_list.is_empty() { if !field_list.is_empty() {
self.report( self.error(
pos, pos,
fa!("the struct initializer is missing {field_list} \ fa!("the struct initializer is missing {field_list} \
(append them to the end of the constructor)"), (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); .map_or_else(|| self.tys.make_array(elem, len as ArrayLen), |_| sty);
if len != fields.len() { if len != fields.len() {
self.report( self.error(
pos, pos,
fa!( fa!(
"expected '{}' but constructor has {} elements", "expected '{}' but constructor has {} elements",
@ -3497,7 +3507,7 @@ impl<'a> Codegen<'a> {
} }
_ => { _ => {
let inferred = if ty.is_some() { "" } else { "inferred " }; let inferred = if ty.is_some() { "" } else { "inferred " };
self.report( self.error(
pos, pos,
fa!( fa!(
"the {inferred}type of the constructor is `{}`, \ "the {inferred}type of the constructor is `{}`, \
@ -3521,7 +3531,7 @@ impl<'a> Codegen<'a> {
let ty::Kind::Struct(s) = sty.expand() else { let ty::Kind::Struct(s) = sty.expand() else {
let inferred = if ty.is_some() { "" } else { "inferred " }; let inferred = if ty.is_some() { "" } else { "inferred " };
self.report( self.error(
pos, pos,
fa!( fa!(
"the {inferred}type of the constructor is `{}`, \ "the {inferred}type of the constructor is `{}`, \
@ -3540,7 +3550,7 @@ impl<'a> Codegen<'a> {
let mem = self.new_stack(pos, sty); let mem = self.new_stack(pos, sty);
for field in fields { for field in fields {
let Some(index) = self.tys.find_struct_field(s, field.name) else { let Some(index) = self.tys.find_struct_field(s, field.name) else {
self.report( self.error(
field.pos, field.pos,
fa!("struct '{}' does not have this field", self.ty_display(sty)), 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)); mem::replace(&mut offs[index], (ty::Id::UNDECLARED, field.pos));
if ty == ty::Id::UNDECLARED { if ty == ty::Id::UNDECLARED {
self.report(field.pos, "the struct field is already initialized"); self.error(field.pos, "the struct field is already initialized");
self.report(offset, "previous initialization is here"); self.error(offset, "previous initialization is here");
continue; continue;
} }
@ -3573,7 +3583,7 @@ impl<'a> Codegen<'a> {
.collect::<String>(); .collect::<String>();
if !field_list.is_empty() { 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)) Some(Value::ptr(mem).ty(sty))
@ -3587,7 +3597,7 @@ impl<'a> Codegen<'a> {
ret = ret.and(self.expr(stmt)); ret = ret.and(self.expr(stmt));
if let Some(mut id) = ret { if let Some(mut id) = ret {
if id.ty != ty::Id::VOID { if id.ty != ty::Id::VOID {
self.report( self.error(
stmt.pos(), stmt.pos(),
fa!( fa!(
"statements need to evaluate to 'void', \ "statements need to evaluate to 'void', \
@ -3890,7 +3900,7 @@ impl<'a> Codegen<'a> {
let value = self.expr(value)?; let value = self.expr(value)?;
let ty::Kind::Enum(e) = value.ty.expand() else { let ty::Kind::Enum(e) = value.ty.expand() else {
self.report( self.error(
pos, pos,
fa!( fa!(
"match operates on enums (for now), '{}' is not an enum", "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 { for &MatchBranch { pat, pos: bp, body } in branches {
if let Expr::Wildcard { .. } = pat { if let Expr::Wildcard { .. } = pat {
if let Some(prev) = else_branch { if let Some(prev) = else_branch {
self.report(bp, "duplicate branch"); self.error(bp, "duplicate branch");
self.report(prev.pos(), "...first branch declared here"); self.error(prev.pos(), "...first branch declared here");
} }
else_branch = Some(body); else_branch = Some(body);
@ -3916,8 +3926,8 @@ impl<'a> Codegen<'a> {
let pat_val = self.eval_const(self.ci.file, &pat, value.ty); let pat_val = self.eval_const(self.ci.file, &pat, value.ty);
if covered_values[pat_val as usize] != Pos::MAX { if covered_values[pat_val as usize] != Pos::MAX {
self.report(bp, "duplicate branch"); self.error(bp, "duplicate branch");
self.report( self.error(
covered_values[pat_val as usize], covered_values[pat_val as usize],
"...first branch declared here", "...first branch declared here",
); );
@ -3966,10 +3976,7 @@ impl<'a> Codegen<'a> {
.collect::<String>(); .collect::<String>();
if !missing_branches.is_empty() { if !missing_branches.is_empty() {
self.report( self.error(pos, fa!("not all cases covered, missing '{missing_branches}'"));
pos,
fa!("not all cases covered, missing '{missing_branches}'"),
);
} }
self.ci.ctrl.get() self.ci.ctrl.get()
}; };
@ -3989,7 +3996,7 @@ impl<'a> Codegen<'a> {
Some(Value::VOID) Some(Value::VOID)
} }
ref e => { ref e => {
self.report_unhandled_ast(e, "bruh"); self.error_unhandled_ast(e, "bruh");
Value::NEVER Value::NEVER
} }
} }
@ -4035,7 +4042,7 @@ impl<'a> Codegen<'a> {
.map(|f| self.tys.names.ident_str(f.name)) .map(|f| self.tys.names.ident_str(f.name))
.intersperse("', '") .intersperse("', '")
.collect::<String>(); .collect::<String>();
self.report( self.error(
pos, pos,
fa!( fa!(
"the '{}' does not have this variant, \ "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<Value> { fn gen_call(&mut self, func: &Expr, args: &[Expr], inline: bool) -> Option<Value> {
let ty = self.ty(func); let ty = self.ty(func);
let ty::Kind::Func(mut fu) = ty.expand() else { 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; return Value::NEVER;
}; };
@ -4083,7 +4090,7 @@ impl<'a> Codegen<'a> {
let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else { unreachable!() }; let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else { unreachable!() };
if args.len() != cargs.len() { if args.len() != cargs.len() {
self.report( self.error(
func.pos(), func.pos(),
fa!( fa!(
"expected {} function argumenr{}, got {}", "expected {} function argumenr{}, got {}",
@ -4095,7 +4102,7 @@ impl<'a> Codegen<'a> {
} }
if inline && is_inline { if inline && is_inline {
self.report( self.error(
func.pos(), func.pos(),
"function is declared as inline so this @inline directive only reduces readability", "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 { if sig.ret == ty::Id::VOID {
self.expr(&Expr::Return { pos: body.pos(), val: None }); self.expr(&Expr::Return { pos: body.pos(), val: None });
} else { } else {
self.report( self.error(
body.pos(), body.pos(),
"expected all paths in the fucntion to return \ "expected all paths in the fucntion to return \
or the return type to be 'void'", 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()].kind.is_eca()
|| self.ci.nodes[ctrl.get()].inputs[0] != prev_ctrl) || 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)); 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) => { ty::Kind::Struct(is) => {
if !self.struct_op(pos, op, is, dst, lhs, rhs) { if !self.struct_op(pos, op, is, dst, lhs, rhs) {
self.report( self.error(
pos, pos,
fa!("... when appliing '{0} {op} {0}'", self.ty_display(s.into())), 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) { ty::Kind::Struct(is) => match self.struct_fold_op(pos, op, fold_op, is, lhs, rhs) {
Some(v) => v.id, Some(v) => v.id,
None => { None => {
self.report( self.error(
pos, pos,
fa!("...when appliing '{0} {op} {0}'", self.ty_display(s.into())), 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; return None;
} }
}; };
@ -4398,7 +4405,7 @@ impl<'a> Codegen<'a> {
ty::Id::UNDECLARED ty::Id::UNDECLARED
} else { } else {
if ty != ty::Id::TYPE { if ty != ty::Id::TYPE {
self.report( self.error(
arg.pos(), arg.pos(),
fa!( fa!(
"arbitrary comptime types are not supported yet \ "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 { 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; return None;
}; };
let ret = self.ty_in(file, ret); let ret = self.ty_in(file, ret);
@ -4463,20 +4470,20 @@ impl<'a> Codegen<'a> {
} }
Expr::Ctor { pos, fields, .. } => { Expr::Ctor { pos, fields, .. } => {
let ty::Kind::Struct(idx) = right.ty.expand() else { 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; return;
}; };
for &CtorField { pos, name, ref value } in fields { for &CtorField { pos, name, ref value } in fields {
let Some((offset, ty)) = OffsetIter::offset_of(self.tys, idx, name) else { 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; continue;
}; };
let off = self.offset(right.id, offset); let off = self.offset(right.id, offset);
self.assign_pattern(value, Value::ptr(off).ty(ty)); 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<Value> { fn jump_to(&mut self, pos: Pos, id: usize) -> Option<Value> {
let Some(mut loob) = self.ci.loops.last_mut() else { 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; return None;
}; };
@ -4639,7 +4646,7 @@ impl<'a> Codegen<'a> {
if sig.ret == ty::Id::VOID { if sig.ret == ty::Id::VOID {
self.expr(&Expr::Return { pos: body.pos(), val: None }); self.expr(&Expr::Return { pos: body.pos(), val: None });
} else { } else {
self.report( self.error(
body.pos(), body.pos(),
fa!( fa!(
"expected all paths in the fucntion to return \ "expected all paths in the fucntion to return \
@ -4693,23 +4700,25 @@ impl<'a> Codegen<'a> {
} }
_ => unreachable!(), _ => unreachable!(),
}; };
self.report(pos, msg); self.error(pos, msg);
} }
for &node in self.ci.nodes[NEVER].inputs.iter() { 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 && self.ci.nodes[self.ci.nodes.aclass_index(self.ci.nodes[node].inputs[1]).1].kind
== Kind::Stck == Kind::Stck
{ {
self.report( let pfile = mem::replace(&mut self.ci.file, file);
self.error(
self.ci.nodes[node].pos, self.ci.nodes[node].pos,
"returning value with local provenance \ "returning value with local provenance \
(pointer will be invalid after function returns)", (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, self.ci.nodes[self.ci.nodes.aclass_index(self.ci.nodes[node].inputs[1]).1].pos,
"...the pointer points to stack allocation created here", "...the pointer points to stack allocation created here",
); );
self.ci.file = pfile;
} }
} }
@ -4783,7 +4792,7 @@ impl<'a> Codegen<'a> {
} else { } else {
let ty = self.ty_display(lhs.ty); let ty = self.ty_display(lhs.ty);
let expected = self.ty_display(rhs.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) (ty::Id::NEVER, VOID)
} }
} }
@ -4945,7 +4954,7 @@ impl<'a> Codegen<'a> {
let ty = self.ty_display(src.ty); let ty = self.ty_display(src.ty);
let expected = self.ty_display(expected); 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 false
} }
} }
@ -4958,16 +4967,22 @@ impl<'a> Codegen<'a> {
value.ty = to; 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] #[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(); let mut buf = self.errors.borrow_mut();
write!(buf, "{}", self.file().report(pos, msg)).unwrap(); write!(buf, "{}", self.file().report(pos, msg)).unwrap();
} }
#[track_caller] #[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:#?}"); 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 { fn file(&self) -> &'a parser::Ast {

View file

@ -238,7 +238,7 @@ impl Backend for HbvmBackend {
debug_assert_matches!( debug_assert_matches!(
nodes[stck].kind, nodes[stck].kind,
Kind::Phi Kind::Phi
| Kind::Return | Kind::Return { .. }
| Kind::Load | Kind::Load
| Kind::Call { .. } | Kind::Call { .. }
| Kind::Stre | Kind::Stre
@ -524,7 +524,7 @@ impl HbvmBackend {
self.emit(instrs::jmp(0)); self.emit(instrs::jmp(0));
} }
} }
Kind::Return => { Kind::Return { .. } => {
match retl { match retl {
Some(PLoc::Reg(r, size)) if sig.ret.loc(tys) == Loc::Stack => { Some(PLoc::Reg(r, size)) if sig.ret.loc(tys) == Loc::Stack => {
self.emit(instrs::ld(r, allocs[0], 0, size)) self.emit(instrs::ld(r, allocs[0], 0, size))

View file

@ -183,7 +183,7 @@ impl HbvmBackend {
} }
is_next_block = res.backrefs[nid as usize] as usize == i + 1; is_next_block = res.backrefs[nid as usize] as usize == i + 1;
} }
Kind::Return => { Kind::Return { .. } => {
let &[_, ret, ..] = node.inputs.as_slice() else { unreachable!() }; let &[_, ret, ..] = node.inputs.as_slice() else { unreachable!() };
match retl { match retl {
Some(PLoc::Reg(r, _)) if sig.ret.loc(tys) == Loc::Reg => { Some(PLoc::Reg(r, _)) if sig.ret.loc(tys) == Loc::Reg => {
@ -423,7 +423,7 @@ impl<'a> Function<'a> {
self.emit_node(o); self.emit_node(o);
} }
} }
Kind::Return | Kind::Die => { Kind::Return { .. } | Kind::Die => {
self.close_block(nid); self.close_block(nid);
self.emit_node(node.outputs[0]); self.emit_node(node.outputs[0]);
} }