implementing struct method for non generic contexts

This commit is contained in:
Jakub Doka 2024-11-24 16:17:57 +01:00
parent 58ee5c0a56
commit 24a3aed360
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
2 changed files with 201 additions and 127 deletions

View file

@ -27,6 +27,7 @@ use {
},
hashbrown::hash_map,
hbbytecode::DisasmError,
std::intrinsics::vtable_size,
};
const VOID: Nid = 0;
@ -2954,125 +2955,10 @@ impl<'a> Codegen<'a> {
None
}
Expr::Field { target, name, pos } => {
let mut vtarget = self.raw_expr(target)?;
self.strip_var(&mut vtarget);
self.implicit_unwrap(pos, &mut vtarget);
let tty = vtarget.ty;
match self.tys.base_of(tty).unwrap_or(tty).expand() {
ty::Kind::Module(m) => {
match self.find_type(pos, self.ci.file, m, Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
ty::Kind::Enum(e) => {
let intrnd = self.tys.names.project(name);
self.gen_enum_variant(pos, e, intrnd)
}
ty::Kind::Struct(s) => {
let Struct { ast, file, .. } = self.tys.ins.structs[s];
if let Some((offset, ty)) = OffsetIter::offset_of(self.tys, s, name) {
Some(Value::ptr(self.offset(vtarget.id, offset)).ty(ty))
} else if let Expr::Struct {
fields: [.., CommentOr::Or(Err(scope))], ..
} = ast.get(&self.files[file.index()])
&& let ty = self.find_type_low(
pos,
self.ci.file,
file,
Some((s.into(), scope)),
Err(name),
)
&& ty != ty::Id::NEVER
{
todo!()
} else {
let field_list = self
.tys
.struct_fields(s)
.iter()
.map(|f| self.tys.names.ident_str(f.name))
.intersperse("', '")
.collect::<String>();
self.error(
pos,
fa!(
"the '{}' does not have this field, \
but it does have '{field_list}'",
self.ty_display(tty)
),
);
Value::NEVER
}
}
ty::Kind::TYPE => match ty::Id::from(match self.ci.nodes[vtarget.id].kind {
Kind::CInt { value } => value as u64,
_ => unreachable!(),
})
.expand()
{
ty::Kind::Struct(s) => {
let Struct { ast, file, .. } = self.tys.ins.structs[s];
let Expr::Struct { fields: &[.., CommentOr::Or(Err(scope))], .. } =
ast.get(&self.files[file.index()])
else {
self.error(
pos,
fa!("'{}' has not declarations", self.ty_display(s.into())),
);
return Value::NEVER;
};
match self
.find_type_low(
pos,
self.ci.file,
file,
Some((s.into(), scope)),
Err(name),
)
.expand()
{
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
ty::Kind::Module(m) => {
match self.find_type(pos, self.ci.file, m, Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
ty => {
self.error(
pos,
fa!(
"accesing scope on '{}' is not supported yet",
self.ty_display(ty.compress())
),
);
Value::NEVER
}
},
_ => {
self.error(
pos,
fa!(
"the '{}' is not a struct, or pointer to one, or enum, \
fo field access does not make sense",
self.ty_display(tty)
),
);
Value::NEVER
}
}
self.gen_field(ctx, target, pos, name)?.ok().or_else(|| {
self.error(pos, "method can not be used like this");
Value::NEVER
})
}
Expr::UnOp { op: TokenKind::Band, val, pos } => {
let ctx = Ctx { ty: ctx.ty.and_then(|ty| self.tys.base_of(ty)) };
@ -4158,6 +4044,126 @@ impl<'a> Codegen<'a> {
}
}
fn gen_field(
&mut self,
ctx: Ctx,
target: &Expr,
pos: Pos,
name: &str,
) -> Option<Result<Value, (ty::Id, Value)>> {
let mut vtarget = self.raw_expr(target)?;
self.strip_var(&mut vtarget);
self.implicit_unwrap(pos, &mut vtarget);
let tty = vtarget.ty;
match self.tys.base_of(tty).unwrap_or(tty).expand() {
ty::Kind::Module(m) => match self.find_type(pos, self.ci.file, m, Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
},
ty::Kind::Enum(e) => {
let intrnd = self.tys.names.project(name);
self.gen_enum_variant(pos, e, intrnd)
}
ty::Kind::Struct(s) => {
let Struct { ast, file, .. } = self.tys.ins.structs[s];
if let Some((offset, ty)) = OffsetIter::offset_of(self.tys, s, name) {
Some(Value::ptr(self.offset(vtarget.id, offset)).ty(ty))
} else if let Expr::Struct { fields: [.., CommentOr::Or(Err(scope))], .. } =
ast.get(&self.files[file.index()])
&& let ty = self.find_type_low(
pos,
self.ci.file,
file,
Some((s.into(), scope)),
Err(name),
)
&& let ty::Kind::Func(_) = ty.expand()
{
return Some(Err((ty, vtarget)));
} else {
let field_list = self
.tys
.struct_fields(s)
.iter()
.map(|f| self.tys.names.ident_str(f.name))
.intersperse("', '")
.collect::<String>();
self.error(
pos,
fa!(
"the '{}' does not have this field, \
but it does have '{field_list}'",
self.ty_display(tty)
),
);
Value::NEVER
}
}
ty::Kind::TYPE => match ty::Id::from(match self.ci.nodes[vtarget.id].kind {
Kind::CInt { value } => value as u64,
_ => unreachable!(),
})
.expand()
{
ty::Kind::Struct(s) => {
let Struct { ast, file, .. } = self.tys.ins.structs[s];
let Expr::Struct { fields: &[.., CommentOr::Or(Err(scope))], .. } =
ast.get(&self.files[file.index()])
else {
self.error(
pos,
fa!("'{}' has not declarations", self.ty_display(s.into())),
);
return Value::NEVER.map(Ok);
};
match self
.find_type_low(pos, self.ci.file, file, Some((s.into(), scope)), Err(name))
.expand()
{
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
ty::Kind::Module(m) => {
match self.find_type(pos, self.ci.file, m, Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
v => Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, v.compress())),
}
}
ty => {
self.error(
pos,
fa!(
"accesing scope on '{}' is not supported yet",
self.ty_display(ty.compress())
),
);
Value::NEVER
}
},
_ => {
self.error(
pos,
fa!(
"the '{}' is not a struct, or pointer to one, or enum, \
fo field access does not make sense",
self.ty_display(tty)
),
);
Value::NEVER
}
}
.map(Ok)
}
fn close_if(&mut self, lcntrl: Nid, rcntrl: Nid, mut then_scope: Scope) -> Option<Nid> {
if lcntrl == Nid::MAX && rcntrl == Nid::MAX {
then_scope.clear(&mut self.ci.nodes);
@ -4231,12 +4237,25 @@ impl<'a> Codegen<'a> {
}
fn gen_call(&mut self, func: &Expr, args: &[Expr], inline: bool) -> Option<Value> {
let mut ty = self.expr(func)?;
self.assert_ty(func.pos(), &mut ty, ty::Id::TYPE, "function");
let ty = ty::Id::from(match self.ci.nodes[ty.id].kind {
Kind::CInt { value } => value as u64,
_ => unreachable!(),
});
let (ty, mut caller) = match *func {
Expr::Field { target, pos, name } => {
match self.gen_field(Ctx::default(), target, pos, name)? {
Ok(mut fexpr) => {
self.assert_ty(func.pos(), &mut fexpr, ty::Id::TYPE, "function");
(
ty::Id::from(match self.ci.nodes[fexpr.id].kind {
Kind::CInt { value } => value as u64,
_ => unreachable!(),
}),
None,
)
}
Err((ty, val)) => (ty, Some(val)),
}
}
ref e => (self.ty(e), None),
};
let ty::Kind::Func(mut fu) = ty.expand() else {
self.error(func.pos(), fa!("compiler cant (yet) call '{}'", self.ty_display(ty)));
return Value::NEVER;
@ -4250,14 +4269,15 @@ impl<'a> Codegen<'a> {
let ast = &self.files[file.index()];
let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else { unreachable!() };
if args.len() != cargs.len() {
let arg_count = args.len() + caller.is_some() as usize;
if arg_count != cargs.len() {
self.error(
func.pos(),
fa!(
"expected {} function argumenr{}, got {}",
cargs.len(),
if cargs.len() == 1 { "" } else { "s" },
args.len()
arg_count
),
);
}
@ -4273,6 +4293,32 @@ impl<'a> Codegen<'a> {
if is_inline || inline {
let var_base = self.ci.scope.vars.len();
let aclass_base = self.ci.scope.aclasses.len();
if let Some(caller) = &mut caller
&& let (Some(Arg::Value(ty)), Some(carg)) = (tys.next(self.tys), cargs.next())
{
match (caller.ty.is_pointer(), ty.is_pointer()) {
(true, false) => {
caller.ty = self.tys.base_of(caller.ty).unwrap();
caller.ptr = true;
}
(false, true) => {
caller.ty = self.tys.make_ptr(caller.ty);
caller.ptr = false;
}
_ => {}
}
self.assert_ty(func.pos(), caller, ty, "caller");
self.ci.scope.vars.push(Variable::new(
carg.id,
ty,
caller.ptr,
caller.id,
&mut self.ci.nodes,
))
}
while let (Some(aty), Some(arg)) = (tys.next(self.tys), args.next()) {
let carg = cargs.next().unwrap();
let var = match aty {
@ -4356,6 +4402,27 @@ impl<'a> Codegen<'a> {
self.make_func_reachable(fu);
let mut inps = Vc::from([NEVER]);
let mut clobbered_aliases = BitSet::default();
if let Some(caller) = &mut caller
&& let (Some(Arg::Value(ty)), Some(carg)) = (tys.next(self.tys), cargs.next())
{
match (caller.ty.is_pointer(), ty.is_pointer()) {
(true, false) => {
caller.ty = self.tys.base_of(caller.ty).unwrap();
caller.ptr = true;
}
(false, true) => {
caller.ty = self.tys.make_ptr(caller.ty);
caller.ptr = false;
}
_ => {}
}
self.assert_ty(func.pos(), caller, ty, fa!("caller argument {}", carg.name));
self.add_clobbers(*caller, &mut clobbered_aliases);
self.ci.nodes.lock(caller.id);
inps.push(caller.id);
}
while let (Some(ty), Some(arg)) = (tys.next(self.tys), args.next()) {
let carg = cargs.next().unwrap();
let Arg::Value(ty) = ty else { continue };

View file

@ -0,0 +1,7 @@
main:
LI64 r13, 1d
CP r1, r13
JALA r0, r31, 0a
code size: 32
ret: 1
status: Ok(())