Skip to content

Commit 6e5e6dd

Browse files
Merge pull request #118 from NLnetLabs/optional-values
Optional values and big typecheck refactor
2 parents 01c59d4 + b63f126 commit 6e5e6dd

31 files changed

+2207
-1383
lines changed

src/ast.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub enum Declaration {
2525
}
2626

2727
#[derive(Clone, Debug)]
28-
pub struct Params(pub Vec<(Meta<Identifier>, Meta<Path>)>);
28+
pub struct Params(pub Vec<(Meta<Identifier>, Meta<TypeExpr>)>);
2929

3030
/// The value of a typed record
3131
#[derive(Clone, Debug)]
@@ -53,7 +53,7 @@ pub struct FilterMap {
5353
pub struct FunctionDeclaration {
5454
pub ident: Meta<Identifier>,
5555
pub params: Meta<Params>,
56-
pub ret: Option<Meta<Path>>,
56+
pub ret: Option<Meta<TypeExpr>>,
5757
pub body: Meta<Block>,
5858
}
5959

@@ -83,6 +83,15 @@ pub struct Path {
8383
pub idents: Vec<Meta<Identifier>>,
8484
}
8585

86+
#[derive(Clone, Debug)]
87+
pub enum TypeExpr {
88+
Optional(Box<TypeExpr>),
89+
Path(Meta<Path>, Vec<Meta<TypeExpr>>),
90+
Never,
91+
Unit,
92+
Record(RecordType),
93+
}
94+
8695
/// A Roto expression
8796
#[derive(Clone, Debug)]
8897
pub enum Expr {
@@ -173,7 +182,7 @@ pub enum Pattern {
173182
Underscore,
174183
EnumVariant {
175184
variant: Meta<Identifier>,
176-
data_field: Option<Meta<Identifier>>,
185+
fields: Option<Meta<Vec<Meta<Identifier>>>>,
177186
},
178187
}
179188

@@ -216,14 +225,7 @@ impl From<String> for Identifier {
216225

217226
#[derive(Clone, Debug)]
218227
pub struct RecordType {
219-
pub key_values: Meta<Vec<(Meta<Identifier>, RecordFieldType)>>,
220-
}
221-
222-
#[derive(Clone, Debug)]
223-
pub enum RecordFieldType {
224-
Path(Meta<Path>),
225-
Record(Meta<RecordType>),
226-
List(Meta<Box<RecordFieldType>>),
228+
pub fields: Meta<Vec<(Meta<Identifier>, Meta<TypeExpr>)>>,
227229
}
228230

229231
#[derive(Clone, Debug)]
@@ -236,7 +238,7 @@ pub enum Literal {
236238
Bool(bool),
237239
}
238240

239-
#[derive(Clone, Debug)]
241+
#[derive(Clone, Debug, PartialEq, Eq)]
240242
pub enum BinOp {
241243
/// Logical and (`&&`)
242244
And,

src/codegen/check.rs

+83-36
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use crate::{
66
},
77
typechecker::{
88
info::TypeInfo,
9-
types::{Primitive, Type},
9+
scope::{ResolvedName, ScopeRef},
10+
types::{Type, TypeDefinition},
1011
},
1112
};
1213
use std::{
@@ -105,36 +106,43 @@ fn check_roto_type(
105106
let mut roto_ty = type_info.resolve(roto_ty);
106107

107108
if let Type::IntVar(_) = roto_ty {
108-
roto_ty = Type::Primitive(Primitive::I32);
109+
roto_ty = Type::named("i32", Vec::new());
109110
}
110111

111112
match rust_ty.description {
112113
TypeDescription::Leaf => {
113-
let expected_roto = match rust_ty.type_id {
114-
x if x == BOOL => Type::Primitive(Primitive::Bool),
115-
x if x == U8 => Type::Primitive(Primitive::U8),
116-
x if x == U16 => Type::Primitive(Primitive::U16),
117-
x if x == U32 => Type::Primitive(Primitive::U32),
118-
x if x == U64 => Type::Primitive(Primitive::U64),
119-
x if x == I8 => Type::Primitive(Primitive::I8),
120-
x if x == I16 => Type::Primitive(Primitive::I16),
121-
x if x == I32 => Type::Primitive(Primitive::I32),
122-
x if x == I64 => Type::Primitive(Primitive::I64),
123-
x if x == UNIT => Type::Primitive(Primitive::Unit),
124-
x if x == ASN => Type::Primitive(Primitive::Asn),
125-
x if x == IPADDR => Type::Primitive(Primitive::IpAddr),
126-
x if x == PREFIX => Type::Primitive(Primitive::Prefix),
127-
x if x == STRING => Type::Primitive(Primitive::String),
114+
let expected_name = match rust_ty.type_id {
115+
x if x == BOOL => "bool",
116+
x if x == U8 => "u8",
117+
x if x == U16 => "u16",
118+
x if x == U32 => "u32",
119+
x if x == U64 => "u64",
120+
x if x == I8 => "i8",
121+
x if x == I16 => "i16",
122+
x if x == I32 => "i32",
123+
x if x == I64 => "i64",
124+
x if x == UNIT => "Unit",
125+
x if x == ASN => "Asn",
126+
x if x == IPADDR => "IpAddr",
127+
x if x == PREFIX => "Prefix",
128+
x if x == STRING => "String",
128129
_ => panic!(),
129130
};
131+
let expected_roto = Type::named(expected_name, Vec::new());
130132
if expected_roto == roto_ty {
131133
Ok(())
132134
} else {
133135
Err(error_message)
134136
}
135137
}
136138
TypeDescription::Val(ty) => {
137-
let Type::BuiltIn(_, id) = roto_ty else {
139+
let Type::Name(type_name) = roto_ty else {
140+
return Err(error_message);
141+
};
142+
143+
let TypeDefinition::Runtime(_, id) =
144+
type_info.resolve_type_name(&type_name)
145+
else {
138146
return Err(error_message);
139147
};
140148

@@ -147,18 +155,48 @@ fn check_roto_type(
147155
TypeDescription::ConstPtr(_) => Err(error_message),
148156
TypeDescription::MutPtr(_) => Err(error_message), // TODO: actually check this
149157
TypeDescription::Verdict(rust_accept, rust_reject) => {
150-
let Type::Verdict(roto_accept, roto_reject) = &roto_ty else {
158+
let Type::Name(type_name) = &roto_ty else {
151159
return Err(error_message);
152160
};
161+
162+
if type_name.name
163+
!= (ResolvedName {
164+
scope: ScopeRef::GLOBAL,
165+
ident: "Verdict".into(),
166+
})
167+
{
168+
return Err(error_message);
169+
}
170+
171+
let [roto_accept, roto_reject] = &type_name.arguments[..] else {
172+
return Err(error_message);
173+
};
174+
153175
check_roto_type(registry, type_info, rust_accept, roto_accept)?;
154176
check_roto_type(registry, type_info, rust_reject, roto_reject)?;
155177
Ok(())
156178
}
157-
// We don't do options and results, we should hint towards verdict
158-
// when using them.
159-
TypeDescription::Option(_) | TypeDescription::Result(_, _) => {
160-
Err(error_message)
179+
TypeDescription::Option(rust_ty) => {
180+
let Type::Name(type_name) = &roto_ty else {
181+
return Err(error_message);
182+
};
183+
184+
if type_name.name
185+
!= (ResolvedName {
186+
scope: ScopeRef::GLOBAL,
187+
ident: "Optional".into(),
188+
})
189+
{
190+
return Err(error_message);
191+
}
192+
193+
let [roto_ty] = &type_name.arguments[..] else {
194+
return Err(error_message);
195+
};
196+
check_roto_type(registry, type_info, rust_ty, roto_ty)
161197
}
198+
// We don't do results, we should hint towards verdict when using them.
199+
TypeDescription::Result(_, _) => Err(error_message),
162200
}
163201
}
164202

@@ -172,18 +210,20 @@ fn check_roto_type(
172210
///
173211
/// This trait is implemented on tuples of various sizes.
174212
pub trait RotoParams {
213+
type Transformed;
175214
/// This type but with [`Reflect::AsParam`] applied to each element.
176215
type AsParams;
177216

217+
fn transform(self) -> Self::Transformed;
218+
178219
/// Convert to `Self::AsParams`.
179-
fn as_params(&mut self) -> Self::AsParams;
220+
fn as_params(transformed: &mut Self::Transformed) -> Self::AsParams;
180221

181222
/// Check whether these parameters match a parameter list from Roto.
182223
fn check(
183224
type_info: &mut TypeInfo,
184225
ty: &[Type],
185226
) -> Result<(), FunctionRetrievalError>;
186-
187227
/// Call a function pointer as if it were a function with these parameters.
188228
///
189229
/// This is _extremely_ unsafe, do not pass this arbitrary pointers and
@@ -214,14 +254,18 @@ macro_rules! params {
214254
#[allow(unused_variables)]
215255
#[allow(unused_mut)]
216256
impl<$($t,)*> RotoParams for ($($t,)*)
217-
where
218-
$($t: Reflect,)*
219-
{
257+
where $($t: Reflect,)* {
258+
type Transformed = ($($t::Transformed,)*);
220259
type AsParams = ($($t::AsParam,)*);
221260

222-
fn as_params(&mut self) -> Self::AsParams {
261+
fn transform(self) -> Self::Transformed {
223262
let ($($t,)*) = self;
224-
return ($($t.as_param(),)*);
263+
return ($($t.transform(),)*);
264+
}
265+
266+
fn as_params(transformed: &mut Self::Transformed) -> Self::AsParams {
267+
let ($($t,)*) = transformed;
268+
return ($($t::as_param($t),)*);
225269
}
226270

227271
fn check(
@@ -246,21 +290,24 @@ macro_rules! params {
246290
Ok(())
247291
}
248292

249-
unsafe fn invoke<Ctx: 'static, Return: Reflect>(mut self, ctx: &mut Ctx, func_ptr: *const u8, return_by_ref: bool) -> Return {
250-
let ($($t,)*) = self.as_params();
293+
unsafe fn invoke<Ctx: 'static, Return: Reflect>(self, ctx: &mut Ctx, func_ptr: *const u8, return_by_ref: bool) -> Return {
294+
let mut transformed = <Self as RotoParams>::transform(self);
295+
let ($($t,)*) = <Self as RotoParams>::as_params(&mut transformed);
251296

252297
// We forget values that we pass into Roto. The script is responsible
253298
// for cleaning them op. Forgetting copy types does nothing, but that's
254299
// fine.
255300
#[allow(forgetting_copy_types)]
256-
std::mem::forget(self);
301+
std::mem::forget(transformed);
257302
if return_by_ref {
258303
let func_ptr = unsafe {
259-
std::mem::transmute::<*const u8, fn(*mut Return, *mut Ctx, $($t::AsParam),*) -> ()>(func_ptr)
304+
std::mem::transmute::<*const u8, fn(*mut Return::Transformed, *mut Ctx, $($t::AsParam),*) -> ()>(func_ptr)
260305
};
261-
let mut ret = MaybeUninit::<Return>::uninit();
306+
let mut ret = MaybeUninit::<Return::Transformed>::uninit();
262307
func_ptr(ret.as_mut_ptr(), ctx as *mut Ctx, $($t),*);
263-
unsafe { ret.assume_init() }
308+
let transformed_ret = unsafe { ret.assume_init() };
309+
let ret: Return = Return::untransform(transformed_ret);
310+
ret
264311
} else {
265312
let func_ptr = unsafe {
266313
std::mem::transmute::<*const u8, fn(*mut Ctx, $($t::AsParam),*) -> Return>(func_ptr)

src/codegen/mod.rs

+10-23
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ impl<'c> FuncGen<'c> {
768768
}
769769
ir::Instruction::Div {
770770
to,
771-
ty,
771+
signed,
772772
left,
773773
right,
774774
} => {
@@ -777,14 +777,9 @@ impl<'c> FuncGen<'c> {
777777

778778
let var = self.variable(to, left_ty);
779779

780-
let val = match ty {
781-
IrType::I8 | IrType::I16 | IrType::I32 | IrType::I64 => {
782-
self.ins().sdiv(l, r)
783-
}
784-
IrType::U8 | IrType::U16 | IrType::U32 | IrType::U64 => {
785-
self.ins().udiv(l, r)
786-
}
787-
_ => panic!(),
780+
let val = match signed {
781+
true => self.ins().sdiv(l, r),
782+
false => self.ins().udiv(l, r),
788783
};
789784
self.def(var, val)
790785
}
@@ -797,34 +792,26 @@ impl<'c> FuncGen<'c> {
797792
self.def(var, val)
798793
}
799794
ir::Instruction::Eq { .. } => todo!(),
800-
ir::Instruction::Alloc {
801-
to,
802-
size,
803-
align_shift,
804-
} => {
795+
ir::Instruction::Alloc { to, layout } => {
805796
let slot =
806797
self.builder.create_sized_stack_slot(StackSlotData::new(
807798
StackSlotKind::ExplicitSlot,
808-
*size,
809-
*align_shift,
799+
layout.size() as u32,
800+
layout.align_shift() as u8,
810801
));
811802

812803
let pointer_ty = self.module.isa.pointer_type();
813804
let var = self.variable(to, pointer_ty);
814805
let p = self.ins().stack_addr(pointer_ty, slot, 0);
815806
self.def(var, p);
816807
}
817-
ir::Instruction::Initialize {
818-
to,
819-
bytes,
820-
align_shift,
821-
} => {
808+
ir::Instruction::Initialize { to, bytes, layout } => {
822809
let pointer_ty = self.module.isa.pointer_type();
823810
let slot =
824811
self.builder.create_sized_stack_slot(StackSlotData::new(
825812
StackSlotKind::ExplicitSlot,
826-
bytes.len() as u32,
827-
*align_shift,
813+
layout.size() as u32,
814+
layout.align_shift() as u8,
828815
));
829816

830817
let data_id = self

0 commit comments

Comments
 (0)