Files
lang/src/ir/upper/validate.rs
2025-05-02 18:18:13 -04:00

170 lines
7.2 KiB
Rust

use super::{Type, UInstrInst, UInstruction, UProgram};
use crate::common::{CompilerMsg, CompilerOutput, FileSpan};
impl UProgram {
pub fn validate(&self, output: &mut CompilerOutput) {
for (id, f) in self.iter_fns() {
self.validate_fn(
&f.instructions,
self.origins.get(id),
&f.ret,
output,
true,
false,
);
}
}
pub fn validate_fn(
&self,
instructions: &[UInstrInst],
origin: FileSpan,
ret: &Type,
output: &mut CompilerOutput,
needs_ret: bool,
breakable: bool,
) {
let mut no_ret = true;
for i in instructions {
match &i.i {
UInstruction::Mv { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(src.id);
output.check_assign(self, &src.ty, &dest.ty, i.origin);
}
UInstruction::Ref { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(src.id);
output.check_assign(self, &src.ty.clone().rf(), &dest.ty, i.origin);
}
UInstruction::LoadData { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(*src);
output.check_assign(self, &src.ty, &dest.ty, i.origin);
}
UInstruction::LoadSlice { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(*src);
let Type::Array(srcty, ..) = &src.ty else {
todo!()
};
output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.origin);
}
UInstruction::LoadFn { dst: dest, src } => todo!(),
UInstruction::Call { dst: dest, f, args } => {
let destty = &self.expect(dest.id).ty;
let f = self.expect(f.id);
let Type::Fn { args: argtys, ret } = &f.ty else {
output.err(CompilerMsg {
msg: format!("Type {} is not callable", self.type_name(&f.ty)),
spans: vec![dest.origin],
});
continue;
};
output.check_assign(self, ret, destty, dest.origin);
if args.len() != argtys.len() {
output.err(CompilerMsg {
msg: "Wrong number of arguments to function".to_string(),
spans: vec![dest.origin],
});
}
for (dst_ty, src) in argtys.iter().zip(args) {
let src_var = self.expect(src.id);
output.check_assign(self, &src_var.ty, dst_ty, src.origin);
}
}
UInstruction::AsmBlock { instructions, args } => {
for arg in args {
// TODO: validate size with enabled targets
// maybe should happen in lowering? but I think it could happen here
// if let Some(size) = self.size_of_var(arg.var.id)
// && size != 64
// {
// output.err(CompilerMsg {
// msg: format!("asm block args must be size 64, is size {}", size),
// spans: vec![arg.var.span],
// });
// }
}
}
UInstruction::Ret { src } => {
let srcty = &self.expect(src.id).ty;
output.check_assign(self, srcty, ret, src.origin);
no_ret = false;
}
UInstruction::Construct { dst: dest, fields } => {
let dest_def = self.expect(dest.id);
let sty = match &dest_def.ty {
Type::Struct(sty) => sty,
_ => {
output.err(CompilerMsg {
msg: format!(
"Type {} cannot be constructed",
self.type_name(&dest_def.ty)
),
spans: vec![dest.origin],
});
continue;
}
};
let def = self.expect(sty.id);
for (name, field) in &def.fields {
if let Some(var) = fields.get(name) {
let mut fty = &field.ty;
if let Type::Generic { id } = fty {
for (g, a) in def.generics.iter().zip(&sty.args) {
if *g == *id {
fty = a;
}
}
}
let ety = &self.expect(var.id).ty;
output.check_assign(self, ety, fty, var.origin);
} else {
output.err(CompilerMsg {
msg: format!("field '{}' missing from struct", name),
spans: vec![dest.origin],
});
}
}
}
UInstruction::If { cond, body } => {
let cond = self.expect(cond.id);
output.check_assign(self, &cond.ty, &Type::Bits(64), i.origin);
self.validate_fn(body, origin, ret, output, false, breakable);
}
UInstruction::Loop { body } => {
self.validate_fn(body, origin, ret, output, false, true);
}
UInstruction::Break => {
if !breakable {
output.err(CompilerMsg {
msg: "Can't break here (outside of loop)".to_string(),
spans: vec![i.origin],
});
}
// TODO
}
UInstruction::Continue => {
if !breakable {
output.err(CompilerMsg {
msg: "Can't continue here (outside of loop)".to_string(),
spans: vec![i.origin],
});
}
// TODO
}
}
}
if needs_ret && no_ret && *ret != Type::Unit {
output.err(CompilerMsg {
msg: format!(
"Function implicitly returns () at the end, must return {}",
self.type_name(ret)
),
spans: vec![origin],
});
}
}
}