I can see the light
This commit is contained in:
71
src/ir/id.rs
71
src/ir/id.rs
@@ -1,4 +1,8 @@
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
// I had an idea for why these were different... now I don't
|
||||
pub type Size = u32;
|
||||
@@ -12,19 +16,15 @@ impl<T> ID<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Named {
|
||||
const NAME: &str;
|
||||
}
|
||||
|
||||
impl<T> From<usize> for ID<T> {
|
||||
fn from(value: usize) -> Self {
|
||||
Self(value, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Named> Debug for ID<K> {
|
||||
impl<T> Debug for ID<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}{}", K::NAME, self.0)
|
||||
write!(f, "{{{}}}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,3 +49,60 @@ impl<T> Clone for ID<T> {
|
||||
}
|
||||
|
||||
impl<T> Copy for ID<T> {}
|
||||
|
||||
// :fear:
|
||||
impl<T> Index<ID<T>> for Vec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<ID<T>> for Vec<T> {
|
||||
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&ID<T>> for Vec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&ID<T>> for Vec<T> {
|
||||
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<ID<T>> for [T] {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<ID<T>> for [T] {
|
||||
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&ID<T>> for [T] {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&ID<T>> for [T] {
|
||||
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ impl<'a> LFunctionBuilder<'a> {
|
||||
}
|
||||
pub fn insert_instr(&mut self, i: &UInstrInst) -> Option<Option<String>> {
|
||||
match &i.i {
|
||||
UInstruction::Mv { dest, src } => {
|
||||
UInstruction::Mv { dst: dest, src } => {
|
||||
self.alloc_stack(dest.id)?;
|
||||
self.map_subvar(src.id);
|
||||
self.instrs.push(LInstruction::Mv {
|
||||
@@ -138,7 +138,7 @@ impl<'a> LFunctionBuilder<'a> {
|
||||
src_offset: 0,
|
||||
});
|
||||
}
|
||||
UInstruction::Ref { dest, src } => {
|
||||
UInstruction::Ref { dst: dest, src } => {
|
||||
self.alloc_stack(dest.id)?;
|
||||
self.map_subvar(src.id);
|
||||
self.instrs.push(LInstruction::Ref {
|
||||
@@ -146,7 +146,7 @@ impl<'a> LFunctionBuilder<'a> {
|
||||
src: src.id,
|
||||
});
|
||||
}
|
||||
UInstruction::LoadData { dest, src } => {
|
||||
UInstruction::LoadData { dst: dest, src } => {
|
||||
self.alloc_stack(dest.id)?;
|
||||
let data = self.program.expect(*src);
|
||||
let sym = self.data.builder.ro_data(
|
||||
@@ -161,7 +161,7 @@ impl<'a> LFunctionBuilder<'a> {
|
||||
src: sym,
|
||||
});
|
||||
}
|
||||
UInstruction::LoadSlice { dest, src } => {
|
||||
UInstruction::LoadSlice { dst: dest, src } => {
|
||||
self.alloc_stack(dest.id)?;
|
||||
let data = self.program.expect(*src);
|
||||
let Type::Array(_, len) = &data.ty else {
|
||||
@@ -191,7 +191,7 @@ impl<'a> LFunctionBuilder<'a> {
|
||||
src: sym,
|
||||
});
|
||||
}
|
||||
UInstruction::LoadFn { dest, src } => {
|
||||
UInstruction::LoadFn { dst: dest, src } => {
|
||||
self.alloc_stack(dest.id)?;
|
||||
let sym = self.builder.func(src);
|
||||
self.instrs.push(LInstruction::LoadAddr {
|
||||
@@ -200,7 +200,7 @@ impl<'a> LFunctionBuilder<'a> {
|
||||
src: sym,
|
||||
});
|
||||
}
|
||||
UInstruction::Call { dest, f, args } => {
|
||||
UInstruction::Call { dst: dest, f, args } => {
|
||||
self.alloc_stack(dest.id);
|
||||
self.makes_call = true;
|
||||
let fid = &self.program.fn_var.fun(f.id).expect("a");
|
||||
@@ -267,7 +267,7 @@ impl<'a> LFunctionBuilder<'a> {
|
||||
};
|
||||
self.data.instrs.push(LInstruction::Ret { src })
|
||||
}
|
||||
UInstruction::Construct { dest, fields } => {
|
||||
UInstruction::Construct { dst: dest, fields } => {
|
||||
let sty = &self.program.expect_type(dest.id);
|
||||
let Type::Struct(sty) = sty else {
|
||||
panic!("bruh htis aint' a struct");
|
||||
|
||||
@@ -1,157 +1,31 @@
|
||||
use super::{FnID, Kind, Origin, VarID, NAMED_KINDS};
|
||||
use crate::ir::ID;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct OriginMap {
|
||||
origins: [Vec<Origin>; NAMED_KINDS],
|
||||
}
|
||||
|
||||
impl OriginMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
origins: core::array::from_fn(|_| Vec::new()),
|
||||
}
|
||||
}
|
||||
pub fn get<K: Kind>(&self, id: ID<K>) -> Origin {
|
||||
self.origins[K::INDEX][id.0]
|
||||
}
|
||||
pub fn push<K: Kind>(&mut self, origin: Origin) {
|
||||
self.origins[K::INDEX].push(origin);
|
||||
}
|
||||
}
|
||||
|
||||
pub type NamePath = Vec<String>;
|
||||
|
||||
pub struct NameTree {
|
||||
ids: [HashMap<String, usize>; NAMED_KINDS],
|
||||
children: HashMap<String, NameTree>,
|
||||
}
|
||||
|
||||
impl NameTree {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ids: core::array::from_fn(|_| HashMap::new()),
|
||||
children: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn get(&self, path: &[String]) -> Option<&NameTree> {
|
||||
let first = path.first()?;
|
||||
self.children.get(first)?.get(&path[1..])
|
||||
}
|
||||
pub fn id<K: Kind>(&self, path: &[String], name: &str) -> Option<ID<K>> {
|
||||
self.get(&path)?.ids[K::INDEX]
|
||||
.get(name)
|
||||
.copied()
|
||||
.map(ID::new)
|
||||
}
|
||||
pub fn insert<K: Kind>(&mut self, path: &[String], id: usize) {
|
||||
if let [key] = &path[..] {
|
||||
self.ids[K::INDEX].insert(key.to_string(), id);
|
||||
return;
|
||||
}
|
||||
let Some(key) = path.first() else {
|
||||
return;
|
||||
};
|
||||
self.children
|
||||
.entry(key.to_string())
|
||||
.or_insert_with(|| NameTree::new())
|
||||
.insert::<K>(&path[1..], id);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NameMap {
|
||||
names: [Vec<String>; NAMED_KINDS],
|
||||
tree: NameTree,
|
||||
}
|
||||
|
||||
impl NameMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
names: core::array::from_fn(|_| Vec::new()),
|
||||
tree: NameTree::new(),
|
||||
}
|
||||
}
|
||||
pub fn path<K: Kind>(&self, id: ID<K>) -> &str {
|
||||
&self.names[K::INDEX][id.0]
|
||||
}
|
||||
pub fn name<K: Kind>(&self, id: ID<K>) -> &str {
|
||||
let mut path = self.path(id);
|
||||
while let Some(i) = path.find("::") {
|
||||
path = &path[i + 2..];
|
||||
}
|
||||
path
|
||||
}
|
||||
pub fn id<K: Kind>(&self, path: &[String], name: &str) -> Option<ID<K>> {
|
||||
Some(self.tree.id(path, name)?)
|
||||
}
|
||||
pub fn push<K: Kind>(&mut self, path: &[String]) {
|
||||
let id = self.names[K::INDEX].len();
|
||||
self.tree.insert::<K>(path, id);
|
||||
let name = path.join("::");
|
||||
self.names[K::INDEX].push(name);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FnVarMap {
|
||||
vtf: HashMap<VarID, FnID>,
|
||||
ftv: Vec<VarID>,
|
||||
}
|
||||
|
||||
impl FnVarMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vtf: HashMap::new(),
|
||||
ftv: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn insert(&mut self, f: FnID, v: VarID) {
|
||||
self.vtf.insert(v, f);
|
||||
self.ftv.push(v);
|
||||
}
|
||||
pub fn var(&self, f: FnID) -> VarID {
|
||||
self.ftv[f.0]
|
||||
}
|
||||
pub fn fun(&self, v: VarID) -> Option<FnID> {
|
||||
self.vtf.get(&v).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Ident {
|
||||
id: usize,
|
||||
kind: usize,
|
||||
}
|
||||
|
||||
impl<K: Kind> From<ID<K>> for Ident {
|
||||
fn from(id: ID<K>) -> Self {
|
||||
Self {
|
||||
id: id.0,
|
||||
kind: K::INDEX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this isn't really a map... but also keeps track of "side data"
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Idents {
|
||||
pub latest: Ident,
|
||||
pub kinds: [Option<usize>; NAMED_KINDS],
|
||||
}
|
||||
|
||||
impl Idents {
|
||||
pub fn new(latest: Ident) -> Self {
|
||||
let mut s = Self {
|
||||
latest,
|
||||
kinds: [None; NAMED_KINDS],
|
||||
};
|
||||
s.insert(latest);
|
||||
s
|
||||
}
|
||||
pub fn insert(&mut self, i: Ident) {
|
||||
self.latest = i;
|
||||
self.kinds[i.kind] = Some(i.id);
|
||||
}
|
||||
pub fn get<K: Kind>(&self) -> Option<ID<K>> {
|
||||
self.kinds[K::INDEX].map(|i| i.into())
|
||||
}
|
||||
}
|
||||
// #[derive(Debug, Clone, Copy)]
|
||||
// pub struct Ident {
|
||||
// id: usize,
|
||||
// kind: usize,
|
||||
// }
|
||||
//
|
||||
// // this isn't really a map... but also keeps track of "side data"
|
||||
// #[derive(Debug, Clone, Copy)]
|
||||
// pub struct Idents {
|
||||
// pub latest: Ident,
|
||||
// pub kinds: [Option<usize>; NAMED_KINDS],
|
||||
// }
|
||||
//
|
||||
// impl Idents {
|
||||
// pub fn new(latest: Ident) -> Self {
|
||||
// let mut s = Self {
|
||||
// latest,
|
||||
// kinds: [None; NAMED_KINDS],
|
||||
// };
|
||||
// s.insert(latest);
|
||||
// s
|
||||
// }
|
||||
// pub fn insert(&mut self, i: Ident) {
|
||||
// self.latest = i;
|
||||
// self.kinds[i.kind] = Some(i.id);
|
||||
// }
|
||||
// pub fn get(&self) -> Option<ID<K>> {
|
||||
// self.kinds[K::INDEX].map(|i| i.into())
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use crate::{common::FileSpan, ir::VarID};
|
||||
use crate::ir::VarID;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::UInstruction;
|
||||
use super::{Origin, UInstruction};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct VarInst {
|
||||
pub id: VarID,
|
||||
pub span: FileSpan,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UInstrInst {
|
||||
pub i: UInstruction,
|
||||
pub span: FileSpan,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
impl Debug for VarInst {
|
||||
|
||||
@@ -6,27 +6,31 @@ use crate::{compiler::arch::riscv::Reg, util::Padder};
|
||||
#[derive(Clone)]
|
||||
pub enum UInstruction {
|
||||
Mv {
|
||||
dest: VarInst,
|
||||
dst: VarInst,
|
||||
src: VarInst,
|
||||
},
|
||||
Ref {
|
||||
dest: VarInst,
|
||||
dst: VarInst,
|
||||
src: VarInst,
|
||||
},
|
||||
Deref {
|
||||
dst: VarInst,
|
||||
src: VarInst,
|
||||
},
|
||||
LoadData {
|
||||
dest: VarInst,
|
||||
dst: VarInst,
|
||||
src: DataID,
|
||||
},
|
||||
LoadSlice {
|
||||
dest: VarInst,
|
||||
dst: VarInst,
|
||||
src: DataID,
|
||||
},
|
||||
LoadFn {
|
||||
dest: VarInst,
|
||||
dst: VarInst,
|
||||
src: FnID,
|
||||
},
|
||||
Call {
|
||||
dest: VarInst,
|
||||
dst: VarInst,
|
||||
f: VarInst,
|
||||
args: Vec<VarInst>,
|
||||
},
|
||||
@@ -38,7 +42,7 @@ pub enum UInstruction {
|
||||
src: VarInst,
|
||||
},
|
||||
Construct {
|
||||
dest: VarInst,
|
||||
dst: VarInst,
|
||||
fields: HashMap<String, VarInst>,
|
||||
},
|
||||
If {
|
||||
@@ -68,19 +72,19 @@ pub enum AsmBlockArgType {
|
||||
impl std::fmt::Debug for UInstruction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||
Self::Ref { dest, src } => write!(f, "{dest:?} <- &{src:?}")?,
|
||||
Self::LoadData { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||
Self::LoadFn { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||
Self::LoadSlice { dest, src } => write!(f, "{dest:?} <- &[{src:?}]")?,
|
||||
Self::Mv { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||
Self::Ref { dst: dest, src } => write!(f, "{dest:?} <- &{src:?}")?,
|
||||
Self::LoadData { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||
Self::LoadFn { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||
Self::LoadSlice { dst: dest, src } => write!(f, "{dest:?} <- &[{src:?}]")?,
|
||||
Self::Call {
|
||||
dest,
|
||||
dst: dest,
|
||||
f: func,
|
||||
args,
|
||||
} => write!(f, "{dest:?} <- {func:?}({args:?})")?,
|
||||
Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}")?,
|
||||
Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?,
|
||||
Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?,
|
||||
Self::Construct { dst: dest, fields } => write!(f, "{dest:?} <- {fields:?}")?,
|
||||
Self::If { cond, body } => {
|
||||
write!(f, "if {cond:?}:")?;
|
||||
if !body.is_empty() {
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use super::{Type, UInstrInst, UInstruction, UProgram};
|
||||
use crate::{
|
||||
common::FileSpan,
|
||||
ir::{Len, Named, ID},
|
||||
ir::{Len, ID},
|
||||
};
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
|
||||
pub type NamePath = Vec<String>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UFunc {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
pub args: Vec<VarID>,
|
||||
pub ret: Type,
|
||||
pub argtys: Vec<TypeID>,
|
||||
pub ret: TypeID,
|
||||
pub instructions: Vec<UInstrInst>,
|
||||
}
|
||||
|
||||
@@ -19,16 +24,61 @@ pub struct StructField {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UStruct {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
pub fields: HashMap<String, StructField>,
|
||||
pub generics: Vec<GenericID>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UGeneric {}
|
||||
pub struct UGeneric {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UVar {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
pub ty: TypeID,
|
||||
pub res: UVarTy,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VarParent {
|
||||
id: VarID,
|
||||
path: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MemberID {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ModPath {
|
||||
pub m: ModID,
|
||||
pub path: Vec<MemberID>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum UVarTy {
|
||||
Ptr(VarID),
|
||||
/// fully resolved & typed
|
||||
Res {
|
||||
parent: Option<VarParent>,
|
||||
},
|
||||
/// module doesn't exist yet
|
||||
Unres {
|
||||
path: ModPath,
|
||||
fields: Vec<MemberID>,
|
||||
},
|
||||
/// parent var exists but not typed enough for this field path
|
||||
Partial {
|
||||
v: VarID,
|
||||
fields: Vec<MemberID>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
@@ -39,17 +89,38 @@ pub struct VarOffset {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UData {
|
||||
pub name: String,
|
||||
pub ty: Type,
|
||||
pub content: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UModule {
|
||||
pub name: String,
|
||||
pub members: HashMap<String, MemberID>,
|
||||
pub children: HashMap<String, ModID>,
|
||||
pub parent: Option<ModID>,
|
||||
}
|
||||
|
||||
pub struct ModMissing {
|
||||
pub import_all: Vec<ModID>,
|
||||
pub vars: Vec<VarID>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Member {
|
||||
Fn(FnID),
|
||||
Struct(StructID),
|
||||
Var(VarID),
|
||||
}
|
||||
|
||||
pub type Origin = FileSpan;
|
||||
|
||||
impl UFunc {
|
||||
pub fn ty(&self, program: &UProgram) -> Type {
|
||||
Type::Fn {
|
||||
args: self.args.iter().map(|a| program.expect(*a).ty).collect(),
|
||||
ret: self.ret,
|
||||
args: self.argtys.clone(),
|
||||
ret: Box::new(self.ret.clone()),
|
||||
}
|
||||
}
|
||||
pub fn flat_iter(&self) -> impl Iterator<Item = &UInstrInst> {
|
||||
@@ -85,62 +156,12 @@ impl<'a> Iterator for InstrIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_kind {
|
||||
// TRUST THIS IS SANE!!! KEEP THE CODE DRY AND SAFE!!!!!!
|
||||
($struc:ty, $idx:expr, $field:ident, $name:expr) => {
|
||||
impl_kind!($struc, $idx, $field, $name, nofin);
|
||||
impl Finish for $struc {
|
||||
fn finish(_: &mut UProgram, _: ID<Self>, _: &str) {}
|
||||
}
|
||||
};
|
||||
($struc:ty, $idx:expr, $field:ident, $name:expr, nofin) => {
|
||||
impl Kind for $struc {
|
||||
const INDEX: usize = $idx;
|
||||
fn from_program_mut(program: &mut UProgram) -> &mut Vec<Option<Self>> {
|
||||
&mut program.$field
|
||||
}
|
||||
fn from_program(program: &UProgram) -> &Vec<Option<Self>> {
|
||||
&program.$field
|
||||
}
|
||||
}
|
||||
impl Named for $struc {
|
||||
const NAME: &str = $name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_kind!(UFunc, 0, fns, "func", nofin);
|
||||
impl_kind!(UVar, 1, vars, "var");
|
||||
impl_kind!(UStruct, 2, structs, "struct");
|
||||
impl_kind!(Type, 3, types, "type");
|
||||
impl_kind!(UData, 4, data, "data");
|
||||
pub const NAMED_KINDS: usize = 5;
|
||||
|
||||
pub type FnID = ID<UFunc>;
|
||||
pub type VarID = ID<UVar>;
|
||||
pub type TypeID = ID<Type>;
|
||||
pub type GenericID = ID<UGeneric>;
|
||||
pub type StructID = ID<UStruct>;
|
||||
pub type DataID = ID<UData>;
|
||||
pub type TypeID = ID<Type>;
|
||||
|
||||
impl Finish for UFunc {
|
||||
fn finish(p: &mut UProgram, id: ID<Self>, name: &str) {
|
||||
let var = p.def_searchable(
|
||||
name,
|
||||
Some(UVar {
|
||||
ty: Type::Placeholder,
|
||||
}),
|
||||
p.origins.get(id),
|
||||
);
|
||||
p.fn_var.insert(id, var);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Kind: Sized {
|
||||
const INDEX: usize;
|
||||
fn from_program_mut(program: &mut UProgram) -> &mut Vec<Option<Self>>;
|
||||
fn from_program(program: &UProgram) -> &Vec<Option<Self>>;
|
||||
}
|
||||
|
||||
pub trait Finish: Sized {
|
||||
fn finish(program: &mut UProgram, id: ID<Self>, name: &str);
|
||||
}
|
||||
pub type ModID = ID<Option<UModule>>;
|
||||
|
||||
@@ -6,6 +6,7 @@ mod kind;
|
||||
mod program;
|
||||
mod ty;
|
||||
mod validate;
|
||||
mod resolve;
|
||||
|
||||
use super::*;
|
||||
use assoc::*;
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
pub struct UProgram {
|
||||
// kinds
|
||||
pub fns: Vec<Option<UFunc>>,
|
||||
pub vars: Vec<Option<UVar>>,
|
||||
pub structs: Vec<Option<UStruct>>,
|
||||
pub types: Vec<Option<Type>>,
|
||||
pub data: Vec<Option<UData>>,
|
||||
// associated data
|
||||
pub names: NameMap,
|
||||
pub origins: OriginMap,
|
||||
pub fn_var: FnVarMap,
|
||||
// utils for creation
|
||||
error: Option<TypeID>,
|
||||
pub path: Vec<String>,
|
||||
pub name_stack: Vec<HashMap<String, Idents>>,
|
||||
pub fns: Vec<UFunc>,
|
||||
pub structs: Vec<UStruct>,
|
||||
pub modules: Vec<Option<UModule>>,
|
||||
pub data: Vec<UData>,
|
||||
pub generics: Vec<UGeneric>,
|
||||
pub vars: Vec<UVar>,
|
||||
pub types: Vec<Type>,
|
||||
}
|
||||
|
||||
pub struct UModuleBuilder<'a> {
|
||||
pub p: &'a mut UProgram,
|
||||
pub module: ModID,
|
||||
pub temp: usize,
|
||||
}
|
||||
|
||||
@@ -26,179 +27,64 @@ impl UProgram {
|
||||
vars: Vec::new(),
|
||||
structs: Vec::new(),
|
||||
types: Vec::new(),
|
||||
generics: Vec::new(),
|
||||
data: Vec::new(),
|
||||
names: NameMap::new(),
|
||||
origins: OriginMap::new(),
|
||||
fn_var: FnVarMap::new(),
|
||||
error: None,
|
||||
path: Vec::new(),
|
||||
name_stack: Vec::new(),
|
||||
temp: 0,
|
||||
}
|
||||
}
|
||||
pub fn error_type(&mut self) -> TypeID {
|
||||
self.error
|
||||
.unwrap_or_else(|| self.def("error", Some(Type::Error), Origin::builtin()))
|
||||
}
|
||||
pub fn set_module(&mut self, path: Vec<String>) {
|
||||
self.path = path;
|
||||
self.name_stack = vec![HashMap::new()];
|
||||
}
|
||||
pub fn push(&mut self) {
|
||||
self.name_stack.push(HashMap::new());
|
||||
}
|
||||
pub fn pop(&mut self) {
|
||||
self.name_stack.pop();
|
||||
}
|
||||
pub fn push_name(&mut self, name: &str) {
|
||||
self.path.push(name.to_string());
|
||||
}
|
||||
pub fn pop_name(&mut self) {
|
||||
self.path.pop();
|
||||
}
|
||||
pub fn get_idents(&self, name: &str) -> Option<Idents> {
|
||||
for map in self.name_stack.iter().rev() {
|
||||
let res = map.get(name);
|
||||
if res.is_some() {
|
||||
return res.cloned();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn get<K: Kind>(&self, id: ID<K>) -> Option<&K> {
|
||||
K::from_program(self)[id.0].as_ref()
|
||||
}
|
||||
pub fn get_mut<K: Kind>(&mut self, id: ID<K>) -> Option<&mut K> {
|
||||
K::from_program_mut(self)[id.0].as_mut()
|
||||
}
|
||||
pub fn expect<K: Kind + Named>(&self, id: ID<K>) -> &K {
|
||||
self.get(id)
|
||||
.unwrap_or_else(|| panic!("{id:?} not defined yet!"))
|
||||
}
|
||||
pub fn expect_type(&self, var: VarID) -> &Type {
|
||||
self.expect(self.expect(var).ty)
|
||||
}
|
||||
pub fn expect_mut<K: Kind + Named>(&mut self, id: ID<K>) -> &mut K {
|
||||
self.get_mut(id)
|
||||
.unwrap_or_else(|| panic!("{id:?} not defined yet!"))
|
||||
}
|
||||
pub fn get_fn_var(&self, id: VarID) -> Option<&UFunc> {
|
||||
self.fns[self.fn_var.fun(id)?.0].as_ref()
|
||||
}
|
||||
|
||||
pub fn temp_var(&mut self, origin: Origin, ty: TypeID) -> VarInst {
|
||||
self.temp_var_inner(origin, ty)
|
||||
}
|
||||
|
||||
fn temp_var_inner(&mut self, origin: Origin, ty: TypeID) -> VarInst {
|
||||
let v = self.def(&format!("temp{}", self.temp), Some(UVar { ty }), origin);
|
||||
self.temp += 1;
|
||||
VarInst {
|
||||
id: v,
|
||||
span: origin,
|
||||
modules: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write<K: Kind>(&mut self, id: ID<K>, k: K) {
|
||||
K::from_program_mut(self)[id.0] = Some(k);
|
||||
pub fn instantiate_type(&mut self, ty: Type) {
|
||||
self.def_ty(match ty {
|
||||
Type::Ref(node) => Type::Ref(node.lower()),
|
||||
Type::Generic(node, nodes) => todo!(),
|
||||
Type::Ident(node) => todo!(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn def<K: Kind + Finish>(&mut self, name: &str, k: Option<K>, origin: Origin) -> ID<K> {
|
||||
self.names.push::<K>(&self.path_for(name));
|
||||
self.origins.push::<K>(origin);
|
||||
let vec = K::from_program_mut(self);
|
||||
let id = ID::new(vec.len());
|
||||
vec.push(k);
|
||||
K::finish(self, id, name);
|
||||
pub fn infer(&mut self) -> TypeID {
|
||||
self.def_ty(Type::Infer)
|
||||
}
|
||||
|
||||
pub fn error(&mut self) -> TypeID {
|
||||
self.def_ty(Type::Error)
|
||||
}
|
||||
|
||||
pub fn def_var(&mut self, v: UVar) -> VarID {
|
||||
Self::push_id(&mut self.vars, v)
|
||||
}
|
||||
|
||||
pub fn def_fn(&mut self, f: UFunc) -> FnID {
|
||||
Self::push_id(&mut self.fns, f)
|
||||
}
|
||||
|
||||
pub fn def_ty(&mut self, t: Type) -> TypeID {
|
||||
Self::push_id(&mut self.types, t)
|
||||
}
|
||||
|
||||
pub fn def_generic(&mut self, g: UGeneric) -> GenericID {
|
||||
Self::push_id(&mut self.generics, g)
|
||||
}
|
||||
|
||||
pub fn def_data(&mut self, d: UData) -> DataID {
|
||||
Self::push_id(&mut self.data, d)
|
||||
}
|
||||
|
||||
pub fn def_struct(&mut self, s: UStruct) -> StructID {
|
||||
Self::push_id(&mut self.structs, s)
|
||||
}
|
||||
|
||||
fn push_id<T>(v: &mut Vec<T>, t: T) -> ID<T> {
|
||||
let id = ID::new(v.len());
|
||||
v.push(t);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn path_for(&self, name: &str) -> Vec<String> {
|
||||
if self.path.is_empty() {
|
||||
return vec![name.to_string()];
|
||||
}
|
||||
let mut path = self.path.clone();
|
||||
path.push(name.to_string());
|
||||
path
|
||||
}
|
||||
|
||||
pub fn def_searchable<K: Kind + Finish>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
k: Option<K>,
|
||||
origin: Origin,
|
||||
) -> ID<K> {
|
||||
let id = self.def(&name, k, origin);
|
||||
self.name_on_stack(id, name.to_string());
|
||||
id
|
||||
}
|
||||
|
||||
// hopefully these are not needed after redoing types
|
||||
// pub fn field_type<'a>(&'a self, ty: &'a Type, field: &str) -> Option<&'a Type> {
|
||||
// if let Type::Struct(sty) = ty {
|
||||
// Some(&self.get(*sty.fields.get(field)?)?.ty)
|
||||
// } else if let Type::Module(path) = ty {
|
||||
// let id = self.names.id::<UVar>(path, field)?;
|
||||
// Some(&self.get(id)?.ty)
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn follow_ref(&self, m: &FieldRef) -> Option<&Type> {
|
||||
// let parent = self.get(m.parent)?;
|
||||
// self.field_type(self.follow_type(&parent.ty)?, &m.name)
|
||||
// }
|
||||
//
|
||||
// pub fn get_type<'a>(&'a self, v: VarID) -> Option<&'a Type> {
|
||||
// self.follow_type(&self.get(v)?.ty)
|
||||
// }
|
||||
//
|
||||
// pub fn set_type(&mut self, mut var: VarID, set: Type) -> Option<()> {
|
||||
// let mut path = Vec::new();
|
||||
// while let Type::Field(parent) = &self.get(var)?.ty {
|
||||
// var = parent.parent;
|
||||
// path.push(parent.name.clone());
|
||||
// }
|
||||
// let mut ty = &mut self.vars[var.0].as_mut()?.ty;
|
||||
// 'outer: while let Type::Struct(sty) = ty {
|
||||
// let Some(name) = path.pop() else {
|
||||
// break;
|
||||
// };
|
||||
// let struc = &self.structs[sty.id.0].as_ref()?;
|
||||
// let field = struc.fields.get(&name)?;
|
||||
// let Type::Generic { id } = field.ty else {
|
||||
// return None;
|
||||
// };
|
||||
// for (i, g) in struc.generics.iter().enumerate() {
|
||||
// if *g == id {
|
||||
// ty = &mut sty.args[i];
|
||||
// continue 'outer;
|
||||
// }
|
||||
// }
|
||||
// return None;
|
||||
// }
|
||||
// *ty = set;
|
||||
// Some(())
|
||||
// }
|
||||
//
|
||||
// pub fn follow_type<'a>(&'a self, ty: &'a Type) -> Option<&'a Type> {
|
||||
// match ty {
|
||||
// Type::Field(m) => {
|
||||
// let parent = self.get(m.parent)?;
|
||||
// self.field_type(self.follow_type(&parent.ty)?, &m.name)
|
||||
// }
|
||||
// ty => Some(ty),
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn type_name(&self, ty: &Type) -> String {
|
||||
pub fn type_name<T: Typer>(&self, ty: T) -> String {
|
||||
let mut str = String::new();
|
||||
match ty {
|
||||
match ty.ty(self) {
|
||||
Type::Struct(ty) => {
|
||||
let base = ty.id;
|
||||
str += &self.structs[ty.id].name;
|
||||
let args = &ty.args;
|
||||
str += self.names.name(base);
|
||||
if let Some(arg) = args.first() {
|
||||
str = str + "<" + &self.type_name(arg);
|
||||
}
|
||||
@@ -221,9 +107,16 @@ impl UProgram {
|
||||
str += &self.type_name(ret);
|
||||
}
|
||||
Type::Ref(t) => {
|
||||
str = str + "&" + &self.type_name(t);
|
||||
str += &self.type_name(t);
|
||||
str += "&";
|
||||
}
|
||||
Type::Deref(t) => {
|
||||
str += &self.type_name(t);
|
||||
str += "^";
|
||||
}
|
||||
Type::Unres(_) => {
|
||||
str += "{unresolved}";
|
||||
}
|
||||
Type::Generic { id } => str += self.names.name(*id),
|
||||
Type::Bits(size) => str += &format!("b{}", size),
|
||||
Type::Array(t, len) => str += &format!("[{}; {len}]", self.type_name(t)),
|
||||
Type::Unit => str += "()",
|
||||
@@ -231,50 +124,111 @@ impl UProgram {
|
||||
Type::Error => str += "{error}",
|
||||
Type::Infer => str += "{inferred}",
|
||||
Type::Placeholder => str += "{placeholder}",
|
||||
Type::Module(path) => str += &path.join("::"),
|
||||
Type::Field(m) => {
|
||||
str += &self
|
||||
.follow_ref(m)
|
||||
.map(|t| self.type_name(t))
|
||||
.unwrap_or("{error}".to_string())
|
||||
}
|
||||
}
|
||||
str
|
||||
}
|
||||
pub fn path_var(&self, path: &NamePath, name: &str) -> Option<VarID> {
|
||||
self.names.id(path, name)
|
||||
}
|
||||
pub fn path_ty(&self, path: &NamePath, name: &str) -> Option<&Type> {
|
||||
Some(&self.get(self.path_var(path, name)?)?.ty)
|
||||
}
|
||||
fn name_on_stack<K: Kind>(&mut self, id: ID<K>, name: String) {
|
||||
let idx = self.name_stack.len() - 1;
|
||||
let last = &mut self.name_stack[idx];
|
||||
if let Some(l) = last.get_mut(&name) {
|
||||
l.insert(id.into());
|
||||
} else {
|
||||
last.insert(name, Idents::new(id.into()));
|
||||
}
|
||||
|
||||
impl<'a> UModuleBuilder<'a> {
|
||||
pub fn new(program: &'a mut UProgram, id: ModID, error: TypeID) -> Self {
|
||||
Self {
|
||||
p: program,
|
||||
module: id,
|
||||
error,
|
||||
name_stack: Vec::new(),
|
||||
temp: 0,
|
||||
}
|
||||
}
|
||||
pub fn iter_vars(&self) -> impl Iterator<Item = (VarID, &UVar)> {
|
||||
self.vars
|
||||
.iter()
|
||||
.flatten()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (ID::new(i), x))
|
||||
pub fn push(&mut self) {
|
||||
self.name_stack.push(HashMap::new());
|
||||
}
|
||||
pub fn iter_fns(&self) -> impl Iterator<Item = (FnID, &UFunc)> {
|
||||
self.fns
|
||||
.iter()
|
||||
.flatten()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (ID::new(i), x))
|
||||
pub fn pop(&mut self) {
|
||||
self.name_stack.pop();
|
||||
}
|
||||
pub fn cloned_fns(&self) -> impl Iterator<Item = (FnID, UFunc)> + use<'_> {
|
||||
self.fns
|
||||
.iter()
|
||||
.flatten()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (ID::new(i), x.clone()))
|
||||
pub fn get_idents(&self, name: &str) -> Option<Idents> {
|
||||
for map in self.name_stack.iter().rev() {
|
||||
let res = map.get(name);
|
||||
if res.is_some() {
|
||||
return res.cloned();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn def_var(&mut self, name: &str, v: UVar, origin: Origin) -> VarID {
|
||||
let id = self.p.def_var(name, v, origin);
|
||||
self.name_on_stack(id, name.to_string());
|
||||
id
|
||||
}
|
||||
|
||||
pub fn temp_var<T: Typable>(&mut self, origin: Origin, ty: T) -> VarInst {
|
||||
self.temp_var_inner(origin, ty)
|
||||
}
|
||||
fn temp_var_inner<T: Typable>(&mut self, origin: Origin, ty: T) -> VarInst {
|
||||
let t = self.temp;
|
||||
let v = self
|
||||
.p
|
||||
.def_var(&format!("temp{}", t), UVar { ty: ty.ty(self) }, origin);
|
||||
self.temp += 1;
|
||||
VarInst { id: v, origin }
|
||||
}
|
||||
}
|
||||
|
||||
// I'm done with names...
|
||||
pub trait Typer {
|
||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type;
|
||||
}
|
||||
|
||||
impl Typer for &Type {
|
||||
fn ty(&self, _: &UProgram) -> &Type {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Typer for TypeID {
|
||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
|
||||
&p.types[self]
|
||||
}
|
||||
}
|
||||
|
||||
impl Typer for &TypeID {
|
||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
|
||||
&p.types[*self]
|
||||
}
|
||||
}
|
||||
|
||||
impl Typer for &Box<Type> {
|
||||
fn ty<'a>(&'a self, _: &'a UProgram) -> &'a Type {
|
||||
&**self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Typable {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID;
|
||||
}
|
||||
|
||||
impl Typable for Type {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID {
|
||||
p.def_ty(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Typable for TypeID {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UModuleBuilder<'_> {
|
||||
type Target = UProgram;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.p
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for UModuleBuilder<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.p
|
||||
}
|
||||
}
|
||||
|
||||
343
src/ir/upper/resolve.rs
Normal file
343
src/ir/upper/resolve.rs
Normal file
@@ -0,0 +1,343 @@
|
||||
use super::{Origin, StructTy, Type, TypeID, TypeIDed, UInstruction, UProgram, UStruct, UVar};
|
||||
use crate::common::{CompilerMsg, CompilerOutput};
|
||||
use std::{collections::HashMap, ops::BitOrAssign};
|
||||
|
||||
impl UProgram {
|
||||
pub fn resolve(&mut self, output: &mut CompilerOutput) {
|
||||
let mut unfinished = Vec::new();
|
||||
let mut unfinished_new = Vec::new();
|
||||
let data = &mut ResData {
|
||||
changed: false,
|
||||
types: &mut self.types,
|
||||
vars: &self.vars,
|
||||
structs: &self.structs,
|
||||
errs: Vec::new(),
|
||||
};
|
||||
for f in &self.fns {
|
||||
for i in f.flat_iter() {
|
||||
if resolve_instr(data, &i.i).unfinished() {
|
||||
unfinished.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
while !unfinished.is_empty() && data.changed {
|
||||
data.changed = false;
|
||||
for &i in &unfinished {
|
||||
if resolve_instr(data, &i.i).unfinished() {
|
||||
unfinished_new.push(i);
|
||||
}
|
||||
}
|
||||
std::mem::swap(&mut unfinished, &mut unfinished_new);
|
||||
unfinished_new.clear();
|
||||
}
|
||||
for err in &data.errs {
|
||||
match err {
|
||||
&ResErr::Type {
|
||||
dst,
|
||||
src,
|
||||
ref errs,
|
||||
origin,
|
||||
} => {
|
||||
let mut msg = type_assign_err(self, dst, src);
|
||||
for inner in errs {
|
||||
if inner.dst != dst && inner.src != src {
|
||||
msg.push_str("\n ");
|
||||
msg.push_str(&type_assign_err(self, inner.dst, inner.src));
|
||||
}
|
||||
}
|
||||
output.err(CompilerMsg::new(msg, origin));
|
||||
}
|
||||
&ResErr::NotCallable { origin, ty } => {
|
||||
output.err(CompilerMsg::new(
|
||||
format!("Cannot call type {}", self.type_name(ty)),
|
||||
origin,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
for var in &self.vars {
|
||||
match &self.types[var.ty] {
|
||||
Type::Error => output.err(CompilerMsg::new(
|
||||
format!("Var {:?} is error type!", var.name),
|
||||
var.origin,
|
||||
)),
|
||||
Type::Infer => output.err(CompilerMsg::new(
|
||||
format!("Type of {:?} cannot be inferred", var.name),
|
||||
var.origin,
|
||||
)),
|
||||
Type::Placeholder => output.err(CompilerMsg::new(
|
||||
format!("Var {:?} still placeholder!", var.name),
|
||||
var.origin,
|
||||
)),
|
||||
Type::Unres(_) => output.err(CompilerMsg::new(
|
||||
format!("Var {:?} type still unresolved!", var.name),
|
||||
var.origin,
|
||||
)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_assign_err(p: &mut UProgram, dst: TypeID, src: TypeID) -> String {
|
||||
format!(
|
||||
"Cannot assign type {} to {}",
|
||||
p.type_name(src),
|
||||
p.type_name(dst)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn resolve_instr(data: &mut ResData, i: &UInstruction) -> InstrRes {
|
||||
let mut uf = InstrRes::Finished;
|
||||
match &i {
|
||||
UInstruction::Call { dst, f, args } => {
|
||||
let fty = data.vars[f.id].ty;
|
||||
let Type::Fn { args: fargs, ret } = &data.types[fty] else {
|
||||
data.errs.push(ResErr::NotCallable {
|
||||
origin: f.origin,
|
||||
ty: fty,
|
||||
});
|
||||
return InstrRes::Finished;
|
||||
};
|
||||
uf |= data.match_types(dst, ret, dst.origin);
|
||||
for (src, dest) in args.iter().zip(fargs) {
|
||||
uf |= data.match_types(dest, src, src.origin);
|
||||
}
|
||||
}
|
||||
UInstruction::Mv { dst, src } => {
|
||||
uf |= data.match_types(dst, src, src.origin);
|
||||
}
|
||||
UInstruction::Ref { dst, src } => {
|
||||
let Type::Ref(dest_ty) = data.types[data.vars[dst.id].ty] else {
|
||||
// TODO: this is probably a compiler error / should never happen
|
||||
panic!("how could this happen to me (you)");
|
||||
};
|
||||
uf |= data.match_types(dest_ty, src, src.origin);
|
||||
}
|
||||
UInstruction::LoadData { dst, src } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::LoadSlice { dst, src } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::LoadFn { dst, src } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::AsmBlock { instructions, args } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::Ret { .. } => {}
|
||||
UInstruction::Construct { dst, fields } => {
|
||||
let dest_ty = get(vars, dst.id)?;
|
||||
let Type::Struct(sty) = dest_ty else {};
|
||||
let id = sty.id;
|
||||
let Some(struc) = self.get(id) else {};
|
||||
let mut new = HashMap::new();
|
||||
for (name, field) in &struc.fields {
|
||||
let Some(src) = fields.get(name) else {
|
||||
continue;
|
||||
};
|
||||
let src_ty = get(vars, src.id)?;
|
||||
if let Some(ty) = match_types(vars, types, &field.ty, src_ty) {
|
||||
if let Type::Generic { id } = field.ty {
|
||||
new.insert(id, ty.clone());
|
||||
}
|
||||
set(vars, src.id, ty);
|
||||
}
|
||||
}
|
||||
let mut args: Vec<_> = struc
|
||||
.generics
|
||||
.iter()
|
||||
.map(|&id| Type::Generic { id })
|
||||
.collect();
|
||||
for (i, g) in struc.generics.iter().enumerate() {
|
||||
if let Some(ty) = new.remove(g) {
|
||||
args[i] = ty;
|
||||
}
|
||||
}
|
||||
set(vars, dst.id, Type::Struct(StructTy { id, args }));
|
||||
}
|
||||
UInstruction::If { cond, body } => {
|
||||
for i in body {
|
||||
uf |= resolve_instr(data, &i.i);
|
||||
}
|
||||
}
|
||||
UInstruction::Loop { body } => {}
|
||||
UInstruction::Break => {}
|
||||
UInstruction::Continue => {}
|
||||
}
|
||||
uf
|
||||
}
|
||||
|
||||
pub fn match_types<T1: TypeIDed, T2: TypeIDed>(
|
||||
data: &mut TypeResData,
|
||||
dst: T1,
|
||||
src: T2,
|
||||
) -> MatchRes {
|
||||
let dst = dst.type_id(data.vars);
|
||||
let src = src.type_id(data.vars);
|
||||
if dst == src {
|
||||
return MatchRes::Finished;
|
||||
}
|
||||
let error = || MatchRes::Error(vec![TypeMismatch { dst, src }]);
|
||||
match (&data.types[dst], &data.types[src]) {
|
||||
(Type::Error, _) | (_, Type::Error) => MatchRes::Finished,
|
||||
(Type::Placeholder, _) | (_, Type::Placeholder) => MatchRes::Unfinished,
|
||||
(Type::Infer, Type::Infer) => MatchRes::Unfinished,
|
||||
(Type::Infer, x) => {
|
||||
*data.changed = true;
|
||||
data.types[dst] = x.clone();
|
||||
MatchRes::Finished
|
||||
}
|
||||
(x, Type::Infer) => {
|
||||
*data.changed = true;
|
||||
data.types[src] = x.clone();
|
||||
MatchRes::Finished
|
||||
}
|
||||
(Type::Struct(dest), Type::Struct(src)) => {
|
||||
if dest.id != src.id {
|
||||
return error();
|
||||
}
|
||||
let mut finished = true;
|
||||
let mut errors = Vec::new();
|
||||
let dargs = dest.args.clone();
|
||||
let sargs = dest.args.clone();
|
||||
for (darg, sarg) in dargs.iter().zip(&sargs) {
|
||||
match match_types(data, darg, sarg) {
|
||||
MatchRes::Unfinished => finished = false,
|
||||
MatchRes::Error(errs) => errors.extend(errs),
|
||||
MatchRes::Finished => (),
|
||||
}
|
||||
}
|
||||
if finished {
|
||||
if errors.is_empty() {
|
||||
MatchRes::Finished
|
||||
} else {
|
||||
MatchRes::Error(errors)
|
||||
}
|
||||
} else {
|
||||
MatchRes::Unfinished
|
||||
}
|
||||
}
|
||||
(
|
||||
Type::Fn {
|
||||
args: dest_args,
|
||||
ret: dest_ret,
|
||||
},
|
||||
Type::Fn {
|
||||
args: src_args,
|
||||
ret: src_ret,
|
||||
},
|
||||
) => {
|
||||
// TODO
|
||||
MatchRes::Finished
|
||||
}
|
||||
(&Type::Ref(dest), &Type::Ref(src)) => match_types(data, dest, src),
|
||||
(&Type::Slice(dest), &Type::Slice(src)) => match_types(data, dest, src),
|
||||
(&Type::Array(dest, dlen), &Type::Array(src, slen)) => {
|
||||
if dlen == slen {
|
||||
match_types(data, dest, src)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
}
|
||||
_ => error(),
|
||||
}
|
||||
}
|
||||
|
||||
struct ResData<'a> {
|
||||
changed: bool,
|
||||
types: &'a mut [Type],
|
||||
vars: &'a [UVar],
|
||||
structs: &'a [UStruct],
|
||||
errs: Vec<ResErr>,
|
||||
}
|
||||
|
||||
struct TypeResData<'a> {
|
||||
changed: &'a mut bool,
|
||||
types: &'a mut [Type],
|
||||
vars: &'a [UVar],
|
||||
structs: &'a [UStruct],
|
||||
}
|
||||
|
||||
enum ResErr {
|
||||
NotCallable {
|
||||
origin: Origin,
|
||||
ty: TypeID,
|
||||
},
|
||||
Type {
|
||||
dst: TypeID,
|
||||
src: TypeID,
|
||||
errs: Vec<TypeMismatch>,
|
||||
origin: Origin,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> ResData<'a> {
|
||||
pub fn match_types<T1: TypeIDed, T2: TypeIDed>(
|
||||
&'a mut self,
|
||||
dst: T1,
|
||||
src: T2,
|
||||
origin: Origin,
|
||||
) -> InstrRes {
|
||||
let dst = dst.type_id(self.vars);
|
||||
let src = src.type_id(self.vars);
|
||||
let res = match_types(
|
||||
&mut TypeResData {
|
||||
changed: &mut self.changed,
|
||||
types: self.types,
|
||||
vars: self.vars,
|
||||
structs: self.structs,
|
||||
},
|
||||
dst,
|
||||
src,
|
||||
);
|
||||
match res {
|
||||
MatchRes::Unfinished => InstrRes::Unfinished,
|
||||
MatchRes::Finished => InstrRes::Finished,
|
||||
MatchRes::Error(es) => {
|
||||
self.errs.push(ResErr::Type {
|
||||
errs: es,
|
||||
origin,
|
||||
dst,
|
||||
src,
|
||||
});
|
||||
InstrRes::Finished
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TypeMismatch {
|
||||
dst: TypeID,
|
||||
src: TypeID,
|
||||
}
|
||||
|
||||
pub enum MatchRes {
|
||||
Unfinished,
|
||||
Finished,
|
||||
Error(Vec<TypeMismatch>),
|
||||
}
|
||||
|
||||
pub enum InstrRes {
|
||||
Finished,
|
||||
Unfinished,
|
||||
}
|
||||
|
||||
impl BitOrAssign for InstrRes {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
match rhs {
|
||||
InstrRes::Finished => (),
|
||||
InstrRes::Unfinished => *self = InstrRes::Unfinished,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstrRes {
|
||||
pub fn unfinished(&self) -> bool {
|
||||
match self {
|
||||
Self::Finished => false,
|
||||
Self::Unfinished => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{assoc::NamePath, Len, StructID, TypeID, UInstruction, UProgram, VarID};
|
||||
use super::{FnID, GenericID, Len, ModPath, StructID, TypeID, UVar, VarID, VarInst};
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct FieldRef {
|
||||
@@ -8,211 +6,84 @@ pub struct FieldRef {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct StructTy {
|
||||
pub id: StructID,
|
||||
pub fields: HashMap<String, VarID>,
|
||||
pub args: Vec<TypeID>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone)]
|
||||
pub enum Type {
|
||||
Bits(u32),
|
||||
Struct(StructTy),
|
||||
Fn { args: Vec<TypeID>, ret: TypeID },
|
||||
Fn(FnID),
|
||||
Ref(TypeID),
|
||||
Deref(TypeID),
|
||||
Slice(TypeID),
|
||||
Array(TypeID, Len),
|
||||
Unit,
|
||||
// fake types
|
||||
Field(FieldRef),
|
||||
Module(NamePath),
|
||||
// "fake" types
|
||||
Unres(ModPath),
|
||||
Generic(GenericID),
|
||||
Infer,
|
||||
Error,
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
impl TypeID {
|
||||
pub fn rf(self) -> Type {
|
||||
Type::Ref(self)
|
||||
}
|
||||
pub fn derf(self) -> Type {
|
||||
Type::Deref(self)
|
||||
}
|
||||
pub fn arr(self, len: Len) -> Type {
|
||||
Type::Array(self, len)
|
||||
}
|
||||
pub fn slice(self) -> Type {
|
||||
Type::Slice(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn is_resolved(&self) -> bool {
|
||||
!matches!(self, Self::Error | Self::Placeholder | Self::Infer)
|
||||
}
|
||||
}
|
||||
|
||||
impl UProgram {
|
||||
pub fn resolve_types(&mut self) {
|
||||
// PARTIAL BORROWING :SOB:
|
||||
let fns: Vec<_> = self.cloned_fns().collect();
|
||||
for (i, f) in &fns {
|
||||
let vi = self.fn_var.var(*i);
|
||||
self.expect_mut(vi).ty = f.ty(self);
|
||||
}
|
||||
for (i, f) in &fns {
|
||||
let mut redo_iter = Vec::new();
|
||||
let mut redo_new = Vec::new();
|
||||
for i in f.flat_iter() {
|
||||
if self.resolve_instr_types(&i.i).is_none() {
|
||||
redo_iter.push(i);
|
||||
}
|
||||
}
|
||||
while !redo_iter.is_empty() {
|
||||
for &i in &redo_iter {
|
||||
if self.resolve_instr_types(&i.i).is_none() {
|
||||
redo_new.push(i);
|
||||
}
|
||||
}
|
||||
std::mem::swap(&mut redo_iter, &mut redo_new);
|
||||
redo_new.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_instr_types(&mut self, i: &UInstruction) -> Option<()> {
|
||||
'outer: {
|
||||
match &i {
|
||||
UInstruction::Call { dest, f, args } => {
|
||||
let fun = self.get_fn_var(f.id).expect("bruh");
|
||||
self.expect_mut(dest.id).ty = fun.ret.clone();
|
||||
for (src, &dest) in args.iter().zip(&fun.args) {
|
||||
let dest_ty = self.get_type(dest)?;
|
||||
let src_ty = self.get_type(src.id)?;
|
||||
if let Some(ty) = self.match_types(dest_ty, src_ty) {
|
||||
self.expect_mut(dest).ty = ty.clone();
|
||||
set(vars, dest, ty.clone());
|
||||
set(vars, src.id, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
UInstruction::Mv { dest, src } => {
|
||||
let dest_ty = get(vars, dest.id)?;
|
||||
let src_ty = get(vars, src.id)?;
|
||||
if let Some(ty) = self.match_types(dest_ty, src_ty) {
|
||||
set(vars, dest.id, ty.clone());
|
||||
set(vars, src.id, ty);
|
||||
}
|
||||
}
|
||||
UInstruction::Ref { dest, src } => {
|
||||
let dest_ty = get(vars, dest.id)?;
|
||||
let src_ty = get(vars, src.id)?;
|
||||
let Type::Ref(dest_ty) = dest_ty else {
|
||||
break 'outer;
|
||||
};
|
||||
if let Some(ty) = self.match_types(dest_ty, src_ty) {
|
||||
set(vars, dest.id, ty.clone().rf());
|
||||
set(vars, src.id, ty);
|
||||
}
|
||||
}
|
||||
UInstruction::LoadData { dest, src } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::LoadSlice { dest, src } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::LoadFn { dest, src } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::AsmBlock { instructions, args } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::Ret { .. } => {}
|
||||
UInstruction::Construct { dest, fields } => {
|
||||
let dest_ty = get(vars, dest.id)?;
|
||||
let Type::Struct(sty) = dest_ty else {
|
||||
break 'outer;
|
||||
};
|
||||
let id = sty.id;
|
||||
let Some(struc) = self.get(id) else {
|
||||
break 'outer;
|
||||
};
|
||||
let mut new = HashMap::new();
|
||||
for (name, field) in &struc.fields {
|
||||
let Some(src) = fields.get(name) else {
|
||||
continue;
|
||||
};
|
||||
let src_ty = get(vars, src.id)?;
|
||||
if let Some(ty) = self.match_types(&field.ty, src_ty) {
|
||||
if let Type::Generic { id } = field.ty {
|
||||
new.insert(id, ty.clone());
|
||||
}
|
||||
set(vars, src.id, ty);
|
||||
}
|
||||
}
|
||||
let mut args: Vec<_> = struc
|
||||
.generics
|
||||
.iter()
|
||||
.map(|&id| Type::Generic { id })
|
||||
.collect();
|
||||
for (i, g) in struc.generics.iter().enumerate() {
|
||||
if let Some(ty) = new.remove(g) {
|
||||
args[i] = ty;
|
||||
}
|
||||
}
|
||||
set(vars, dest.id, Type::Struct(StructTy { id, args }));
|
||||
}
|
||||
UInstruction::If { cond, body: _ } => {}
|
||||
UInstruction::Loop { body: _ } => {}
|
||||
UInstruction::Break => {}
|
||||
UInstruction::Continue => {}
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn match_types<'a>(&'a self, mut dest: &'a Type, mut src: &'a Type) -> Option<Type> {
|
||||
dest = self.follow_type(dest)?;
|
||||
src = self.follow_type(src)?;
|
||||
if dest == src {
|
||||
return None;
|
||||
}
|
||||
match (dest, src) {
|
||||
(Type::Error, _) | (_, Type::Error) => None,
|
||||
(Type::Placeholder, _) | (_, Type::Placeholder) => None,
|
||||
(Type::Infer, Type::Infer) => None,
|
||||
(Type::Infer, x) | (x, Type::Infer) => Some(x.clone()),
|
||||
// TODO: handle constraints?
|
||||
(Type::Generic { id }, x) | (x, Type::Generic { id }) => Some(x.clone()),
|
||||
(Type::Struct(dest), Type::Struct(src)) => {
|
||||
if dest.id != src.id {
|
||||
return None;
|
||||
}
|
||||
let mut args = Vec::new();
|
||||
let mut changed = false;
|
||||
for (darg, sarg) in dest.args.iter().zip(&src.args) {
|
||||
if let Some(ty) = self.match_types(darg, sarg) {
|
||||
args.push(ty);
|
||||
changed = true;
|
||||
} else if darg != sarg {
|
||||
return None;
|
||||
} else {
|
||||
args.push(darg.clone());
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
Some(Type::Struct(StructTy { id: dest.id, args }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(
|
||||
Type::Fn {
|
||||
args: dest_args,
|
||||
ret: dest_ret,
|
||||
},
|
||||
Type::Fn {
|
||||
args: src_args,
|
||||
ret: src_ret,
|
||||
},
|
||||
) => {
|
||||
// TODO
|
||||
None
|
||||
}
|
||||
(Type::Ref(dest), Type::Ref(src)) => Some(self.match_types(dest, src)?.rf()),
|
||||
(Type::Slice(dest), Type::Slice(src)) => Some(self.match_types(dest, src)?.slice()),
|
||||
(Type::Array(dest, dlen), Type::Array(src, slen)) => {
|
||||
if dlen != slen {
|
||||
return None;
|
||||
}
|
||||
Some(self.match_types(dest, src)?.arr(*dlen))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
pub fn bx(self) -> Box<Self> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TypeIDed {
|
||||
fn type_id(&self, vars: &[UVar]) -> TypeID;
|
||||
}
|
||||
|
||||
impl TypeIDed for TypeID {
|
||||
fn type_id(&self, _: &[UVar]) -> TypeID {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeIDed for &TypeID {
|
||||
fn type_id(&self, _: &[UVar]) -> TypeID {
|
||||
**self
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeIDed for VarID {
|
||||
fn type_id(&self, vars: &[UVar]) -> TypeID {
|
||||
vars[self].ty
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeIDed for VarInst {
|
||||
fn type_id(&self, vars: &[UVar]) -> TypeID {
|
||||
self.id.type_id(vars)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeIDed for &VarInst {
|
||||
fn type_id(&self, vars: &[UVar]) -> TypeID {
|
||||
self.id.type_id(vars)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,26 +13,6 @@ impl UProgram {
|
||||
false,
|
||||
);
|
||||
}
|
||||
for (id, var) in self.iter_vars() {
|
||||
if var.ty == Type::Error {
|
||||
output.err(CompilerMsg {
|
||||
msg: format!("Var {:?} is error type!", id),
|
||||
spans: vec![self.origins.get(id)],
|
||||
});
|
||||
}
|
||||
if var.ty == Type::Infer {
|
||||
output.err(CompilerMsg {
|
||||
msg: format!("Var {:?} cannot be inferred!", id),
|
||||
spans: vec![self.origins.get(id)],
|
||||
});
|
||||
}
|
||||
if var.ty == Type::Placeholder {
|
||||
output.err(CompilerMsg {
|
||||
msg: format!("Var {:?} still placeholder!", id),
|
||||
spans: vec![self.origins.get(id)],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_fn(
|
||||
@@ -47,50 +27,50 @@ impl UProgram {
|
||||
let mut no_ret = true;
|
||||
for i in instructions {
|
||||
match &i.i {
|
||||
UInstruction::Mv { dest, src } => {
|
||||
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.span);
|
||||
output.check_assign(self, &src.ty, &dest.ty, i.origin);
|
||||
}
|
||||
UInstruction::Ref { dest, src } => {
|
||||
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.span);
|
||||
output.check_assign(self, &src.ty.clone().rf(), &dest.ty, i.origin);
|
||||
}
|
||||
UInstruction::LoadData { dest, src } => {
|
||||
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.span);
|
||||
output.check_assign(self, &src.ty, &dest.ty, i.origin);
|
||||
}
|
||||
UInstruction::LoadSlice { dest, src } => {
|
||||
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.span);
|
||||
output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.origin);
|
||||
}
|
||||
UInstruction::LoadFn { dest, src } => todo!(),
|
||||
UInstruction::Call { dest, f, args } => {
|
||||
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.span],
|
||||
spans: vec![dest.origin],
|
||||
});
|
||||
continue;
|
||||
};
|
||||
output.check_assign(self, ret, destty, dest.span);
|
||||
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.span],
|
||||
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.span);
|
||||
output.check_assign(self, &src_var.ty, dst_ty, src.origin);
|
||||
}
|
||||
}
|
||||
UInstruction::AsmBlock { instructions, args } => {
|
||||
@@ -109,10 +89,10 @@ impl UProgram {
|
||||
}
|
||||
UInstruction::Ret { src } => {
|
||||
let srcty = &self.expect(src.id).ty;
|
||||
output.check_assign(self, srcty, ret, src.span);
|
||||
output.check_assign(self, srcty, ret, src.origin);
|
||||
no_ret = false;
|
||||
}
|
||||
UInstruction::Construct { dest, fields } => {
|
||||
UInstruction::Construct { dst: dest, fields } => {
|
||||
let dest_def = self.expect(dest.id);
|
||||
let sty = match &dest_def.ty {
|
||||
Type::Struct(sty) => sty,
|
||||
@@ -122,7 +102,7 @@ impl UProgram {
|
||||
"Type {} cannot be constructed",
|
||||
self.type_name(&dest_def.ty)
|
||||
),
|
||||
spans: vec![dest.span],
|
||||
spans: vec![dest.origin],
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -139,18 +119,18 @@ impl UProgram {
|
||||
}
|
||||
}
|
||||
let ety = &self.expect(var.id).ty;
|
||||
output.check_assign(self, ety, fty, var.span);
|
||||
output.check_assign(self, ety, fty, var.origin);
|
||||
} else {
|
||||
output.err(CompilerMsg {
|
||||
msg: format!("field '{}' missing from struct", name),
|
||||
spans: vec![dest.span],
|
||||
spans: vec![dest.origin],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
UInstruction::If { cond, body } => {
|
||||
let cond = self.expect(cond.id);
|
||||
output.check_assign(self, &cond.ty, &Type::Bits(64), i.span);
|
||||
output.check_assign(self, &cond.ty, &Type::Bits(64), i.origin);
|
||||
self.validate_fn(body, origin, ret, output, false, breakable);
|
||||
}
|
||||
UInstruction::Loop { body } => {
|
||||
@@ -160,7 +140,7 @@ impl UProgram {
|
||||
if !breakable {
|
||||
output.err(CompilerMsg {
|
||||
msg: "Can't break here (outside of loop)".to_string(),
|
||||
spans: vec![i.span],
|
||||
spans: vec![i.origin],
|
||||
});
|
||||
}
|
||||
// TODO
|
||||
@@ -169,7 +149,7 @@ impl UProgram {
|
||||
if !breakable {
|
||||
output.err(CompilerMsg {
|
||||
msg: "Can't continue here (outside of loop)".to_string(),
|
||||
spans: vec![i.span],
|
||||
spans: vec![i.origin],
|
||||
});
|
||||
}
|
||||
// TODO
|
||||
|
||||
Reference in New Issue
Block a user