Support extern constants of any primitive type.

This commit is contained in:
Chris Fallin
2021-09-15 00:01:51 -07:00
parent d0ace640a2
commit 20bc5ca7a8
7 changed files with 222 additions and 61 deletions

View File

@@ -6,8 +6,6 @@
parse instead where we know the polarity of pattern-term args and parse parse instead where we know the polarity of pattern-term args and parse
in-args as exprs. in-args as exprs.
- Support extern constants.
- Look into whether optimizations are possible: - Look into whether optimizations are possible:
- More in-depth fallibility analysis (avoid failure edges where possible) - More in-depth fallibility analysis (avoid failure edges where possible)

View File

@@ -96,6 +96,8 @@ pub enum Pattern {
Var { var: Ident, pos: Pos }, Var { var: Ident, pos: Pos },
/// An operator that matches a constant integer value. /// An operator that matches a constant integer value.
ConstInt { val: i64, pos: Pos }, ConstInt { val: i64, pos: Pos },
/// An operator that matches an external constant value.
ConstPrim { val: Ident, pos: Pos },
/// An application of a type variant or term. /// An application of a type variant or term.
Term { Term {
sym: Ident, sym: Ident,
@@ -166,9 +168,10 @@ impl Pattern {
} }
} }
&Pattern::Var { .. } | &Pattern::Wildcard { .. } | &Pattern::ConstInt { .. } => { &Pattern::Var { .. }
self.clone() | &Pattern::Wildcard { .. }
} | &Pattern::ConstInt { .. }
| &Pattern::ConstPrim { .. } => self.clone(),
&Pattern::MacroArg { .. } => unreachable!(), &Pattern::MacroArg { .. } => unreachable!(),
} }
} }
@@ -208,9 +211,10 @@ impl Pattern {
} }
} }
&Pattern::Var { .. } | &Pattern::Wildcard { .. } | &Pattern::ConstInt { .. } => { &Pattern::Var { .. }
self.clone() | &Pattern::Wildcard { .. }
} | &Pattern::ConstInt { .. }
| &Pattern::ConstPrim { .. } => self.clone(),
&Pattern::MacroArg { index, .. } => macro_args[index].clone(), &Pattern::MacroArg { index, .. } => macro_args[index].clone(),
} }
} }
@@ -218,6 +222,7 @@ impl Pattern {
pub fn pos(&self) -> Pos { pub fn pos(&self) -> Pos {
match self { match self {
&Pattern::ConstInt { pos, .. } &Pattern::ConstInt { pos, .. }
| &Pattern::ConstPrim { pos, .. }
| &Pattern::And { pos, .. } | &Pattern::And { pos, .. }
| &Pattern::Term { pos, .. } | &Pattern::Term { pos, .. }
| &Pattern::BindPattern { pos, .. } | &Pattern::BindPattern { pos, .. }
@@ -280,6 +285,8 @@ pub enum Expr {
Var { name: Ident, pos: Pos }, Var { name: Ident, pos: Pos },
/// A constant integer. /// A constant integer.
ConstInt { val: i64, pos: Pos }, ConstInt { val: i64, pos: Pos },
/// A constant of some other primitive type.
ConstPrim { val: Ident, pos: Pos },
/// The `(let ((var ty val)*) body)` form. /// The `(let ((var ty val)*) body)` form.
Let { Let {
defs: Vec<LetDef>, defs: Vec<LetDef>,
@@ -294,6 +301,7 @@ impl Expr {
&Expr::Term { pos, .. } &Expr::Term { pos, .. }
| &Expr::Var { pos, .. } | &Expr::Var { pos, .. }
| &Expr::ConstInt { pos, .. } | &Expr::ConstInt { pos, .. }
| &Expr::ConstPrim { pos, .. }
| &Expr::Let { pos, .. } => pos, | &Expr::Let { pos, .. } => pos,
} }
} }
@@ -344,6 +352,8 @@ pub enum Extern {
/// The position of this decl. /// The position of this decl.
pos: Pos, pos: Pos,
}, },
/// An external constant: `(const $IDENT type)` form.
Const { name: Ident, ty: Ident, pos: Pos },
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]

View File

@@ -797,6 +797,23 @@ impl<'a> Codegen<'a> {
self.const_int(val, ty) self.const_int(val, ty)
)?; )?;
} }
&ExprInst::ConstPrim { ty, val } => {
let value = Value::Expr {
inst: id,
output: 0,
};
self.define_val(&value, ctx, /* is_ref = */ false, ty);
let name = self.value_name(&value);
let ty_name = self.type_name(ty, /* by_ref = */ false);
writeln!(
code,
"{}let {}: {} = {};",
indent,
name,
ty_name,
self.typeenv.syms[val.index()],
)?;
}
&ExprInst::CreateVariant { &ExprInst::CreateVariant {
ref inputs, ref inputs,
ty, ty,
@@ -955,6 +972,12 @@ impl<'a> Codegen<'a> {
writeln!(code, "{}if {} == {} {{", indent, input, int_val)?; writeln!(code, "{}if {} == {} {{", indent, input, int_val)?;
Ok(false) Ok(false)
} }
&PatternInst::MatchPrim { ref input, val, .. } => {
let input = self.value_by_val(input, ctx);
let sym = &self.typeenv.syms[val.index()];
writeln!(code, "{}if {} == {} {{", indent, input, sym)?;
Ok(false)
}
&PatternInst::MatchVariant { &PatternInst::MatchVariant {
ref input, ref input,
input_ty, input_ty,

View File

@@ -32,6 +32,9 @@ pub enum PatternInst {
int_val: i64, int_val: i64,
}, },
/// Try matching the given value as the given constant. Produces no values.
MatchPrim { input: Value, ty: TypeId, val: Sym },
/// Try matching the given value as the given variant, producing /// Try matching the given value as the given variant, producing
/// `|arg_tys|` values as output. /// `|arg_tys|` values as output.
MatchVariant { MatchVariant {
@@ -69,6 +72,9 @@ pub enum ExprInst {
/// Produce a constant integer. /// Produce a constant integer.
ConstInt { ty: TypeId, val: i64 }, ConstInt { ty: TypeId, val: i64 },
/// Produce a constant extern value.
ConstPrim { ty: TypeId, val: Sym },
/// Create a variant. /// Create a variant.
CreateVariant { CreateVariant {
inputs: Vec<(Value, TypeId)>, inputs: Vec<(Value, TypeId)>,
@@ -96,6 +102,7 @@ impl ExprInst {
pub fn visit_values<F: FnMut(Value)>(&self, mut f: F) { pub fn visit_values<F: FnMut(Value)>(&self, mut f: F) {
match self { match self {
&ExprInst::ConstInt { .. } => {} &ExprInst::ConstInt { .. } => {}
&ExprInst::ConstPrim { .. } => {}
&ExprInst::Construct { ref inputs, .. } &ExprInst::Construct { ref inputs, .. }
| &ExprInst::CreateVariant { ref inputs, .. } => { | &ExprInst::CreateVariant { ref inputs, .. } => {
for (input, _ty) in inputs { for (input, _ty) in inputs {
@@ -143,21 +150,6 @@ impl ExprSequence {
None None
} }
} }
pub fn is_const_variant(&self) -> Option<(TypeId, VariantId)> {
if self.insts.len() == 2 && matches!(&self.insts[1], &ExprInst::Return { .. }) {
match &self.insts[0] {
&ExprInst::CreateVariant {
ref inputs,
ty,
variant,
} if inputs.len() == 0 => Some((ty, variant)),
_ => None,
}
} else {
None
}
}
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@@ -196,6 +188,10 @@ impl PatternSequence {
self.add_inst(PatternInst::MatchInt { input, ty, int_val }); self.add_inst(PatternInst::MatchInt { input, ty, int_val });
} }
fn add_match_prim(&mut self, input: Value, ty: TypeId, val: Sym) {
self.add_inst(PatternInst::MatchPrim { input, ty, val });
}
fn add_match_variant( fn add_match_variant(
&mut self, &mut self,
input: Value, input: Value,
@@ -290,9 +286,15 @@ impl PatternSequence {
// Assert that the value matches the constant integer. // Assert that the value matches the constant integer.
let input_val = input let input_val = input
.to_value() .to_value()
.expect("Cannot match an =var pattern against root term"); .expect("Cannot match an integer pattern against root term");
self.add_match_int(input_val, ty, value); self.add_match_int(input_val, ty, value);
} }
&Pattern::ConstPrim(ty, value) => {
let input_val = input
.to_value()
.expect("Cannot match a constant-primitive pattern against root term");
self.add_match_prim(input_val, ty, value);
}
&Pattern::Term(ty, term, ref args) => { &Pattern::Term(ty, term, ref args) => {
match input { match input {
ValueOrArgs::ImplicitTermFromArgs(termid) => { ValueOrArgs::ImplicitTermFromArgs(termid) => {
@@ -435,6 +437,12 @@ impl ExprSequence {
Value::Expr { inst, output: 0 } Value::Expr { inst, output: 0 }
} }
fn add_const_prim(&mut self, ty: TypeId, val: Sym) -> Value {
let inst = InstId(self.insts.len());
self.add_inst(ExprInst::ConstPrim { ty, val });
Value::Expr { inst, output: 0 }
}
fn add_create_variant( fn add_create_variant(
&mut self, &mut self,
inputs: &[(Value, TypeId)], inputs: &[(Value, TypeId)],
@@ -490,6 +498,7 @@ impl ExprSequence {
log::trace!("gen_expr: expr {:?}", expr); log::trace!("gen_expr: expr {:?}", expr);
match expr { match expr {
&Expr::ConstInt(ty, val) => self.add_const_int(ty, val), &Expr::ConstInt(ty, val) => self.add_const_int(ty, val),
&Expr::ConstPrim(ty, val) => self.add_const_prim(ty, val),
&Expr::Let(_ty, ref bindings, ref subexpr) => { &Expr::Let(_ty, ref bindings, ref subexpr) => {
let mut vars = vars.clone(); let mut vars = vars.clone();
for &(var, _var_ty, ref var_expr) in bindings { for &(var, _var_ty, ref var_expr) in bindings {

View File

@@ -72,6 +72,13 @@ impl<'a> Parser<'a> {
}) })
} }
fn is_const(&self) -> bool {
self.is(|tok| match tok {
&Token::Symbol(ref tok_s) if tok_s.starts_with("$") => true,
_ => false,
})
}
fn lparen(&mut self) -> ParseResult<()> { fn lparen(&mut self) -> ParseResult<()> {
self.take(|tok| *tok == Token::LParen).map(|_| ()) self.take(|tok| *tok == Token::LParen).map(|_| ())
} }
@@ -129,20 +136,20 @@ impl<'a> Parser<'a> {
fn str_to_ident(&self, pos: Pos, s: &str) -> ParseResult<Ident> { fn str_to_ident(&self, pos: Pos, s: &str) -> ParseResult<Ident> {
let first = s.chars().next().unwrap(); let first = s.chars().next().unwrap();
if !first.is_alphabetic() && first != '_' { if !first.is_alphabetic() && first != '_' && first != '$' {
return Err(self.error( return Err(self.error(
pos, pos,
format!("Identifier '{}' does not start with letter or _", s), format!("Identifier '{}' does not start with letter or _ or $", s),
)); ));
} }
if s.chars() if s.chars()
.skip(1) .skip(1)
.any(|c| !c.is_alphanumeric() && c != '_' && c != '.') .any(|c| !c.is_alphanumeric() && c != '_' && c != '.' && c != '$')
{ {
return Err(self.error( return Err(self.error(
pos, pos,
format!( format!(
"Identifier '{}' contains invalid character (not a-z, A-Z, 0-9, _, .)", "Identifier '{}' contains invalid character (not a-z, A-Z, 0-9, _, ., $)",
s s
), ),
)); ));
@@ -156,6 +163,20 @@ impl<'a> Parser<'a> {
self.str_to_ident(pos.unwrap(), &s) self.str_to_ident(pos.unwrap(), &s)
} }
fn parse_const(&mut self) -> ParseResult<Ident> {
let pos = self.pos();
let ident = self.parse_ident()?;
if ident.0.starts_with("$") {
let s = &ident.0[1..];
Ok(Ident(s.to_string(), ident.1))
} else {
Err(self.error(
pos.unwrap(),
"Not a constant identifier; must start with a '$'".to_string(),
))
}
}
fn parse_type(&mut self) -> ParseResult<Type> { fn parse_type(&mut self) -> ParseResult<Type> {
let pos = self.pos(); let pos = self.pos();
let name = self.parse_ident()?; let name = self.parse_ident()?;
@@ -303,6 +324,16 @@ impl<'a> Parser<'a> {
arg_polarity, arg_polarity,
infallible, infallible,
}) })
} else if self.is_sym_str("const") {
self.symbol()?;
let pos = self.pos();
let name = self.parse_const()?;
let ty = self.parse_ident()?;
Ok(Extern::Const {
name,
ty,
pos: pos.unwrap(),
})
} else { } else {
Err(self.error( Err(self.error(
pos.unwrap(), pos.unwrap(),
@@ -355,6 +386,10 @@ impl<'a> Parser<'a> {
val: self.int()?, val: self.int()?,
pos, pos,
}) })
} else if self.is_const() {
let pos = pos.unwrap();
let val = self.parse_const()?;
Ok(Pattern::ConstPrim { val, pos })
} else if self.is_sym_str("_") { } else if self.is_sym_str("_") {
let pos = pos.unwrap(); let pos = pos.unwrap();
self.symbol()?; self.symbol()?;
@@ -448,6 +483,10 @@ impl<'a> Parser<'a> {
let pos = pos.unwrap(); let pos = pos.unwrap();
self.symbol()?; self.symbol()?;
Ok(Expr::ConstInt { val: 0, pos }) Ok(Expr::ConstInt { val: 0, pos })
} else if self.is_const() {
let pos = pos.unwrap();
let val = self.parse_const()?;
Ok(Expr::ConstPrim { val, pos })
} else if self.is_sym() { } else if self.is_sym() {
let pos = pos.unwrap(); let pos = pos.unwrap();
let name = self.parse_ident()?; let name = self.parse_ident()?;

View File

@@ -35,6 +35,7 @@ pub struct TypeEnv {
pub sym_map: HashMap<String, Sym>, pub sym_map: HashMap<String, Sym>,
pub types: Vec<Type>, pub types: Vec<Type>,
pub type_map: HashMap<Sym, TypeId>, pub type_map: HashMap<Sym, TypeId>,
pub const_types: HashMap<Sym, TypeId>,
pub errors: Vec<Error>, pub errors: Vec<Error>,
} }
@@ -238,6 +239,7 @@ pub enum Pattern {
BindPattern(TypeId, VarId, Box<Pattern>), BindPattern(TypeId, VarId, Box<Pattern>),
Var(TypeId, VarId), Var(TypeId, VarId),
ConstInt(TypeId, i64), ConstInt(TypeId, i64),
ConstPrim(TypeId, Sym),
Term(TypeId, TermId, Vec<TermArgPattern>), Term(TypeId, TermId, Vec<TermArgPattern>),
Wildcard(TypeId), Wildcard(TypeId),
And(TypeId, Vec<Pattern>), And(TypeId, Vec<Pattern>),
@@ -254,6 +256,7 @@ pub enum Expr {
Term(TypeId, TermId, Vec<Expr>), Term(TypeId, TermId, Vec<Expr>),
Var(TypeId, VarId), Var(TypeId, VarId),
ConstInt(TypeId, i64), ConstInt(TypeId, i64),
ConstPrim(TypeId, Sym),
Let(TypeId, Vec<(VarId, TypeId, Box<Expr>)>, Box<Expr>), Let(TypeId, Vec<(VarId, TypeId, Box<Expr>)>, Box<Expr>),
} }
@@ -263,6 +266,7 @@ impl Pattern {
&Self::BindPattern(t, ..) => t, &Self::BindPattern(t, ..) => t,
&Self::Var(t, ..) => t, &Self::Var(t, ..) => t,
&Self::ConstInt(t, ..) => t, &Self::ConstInt(t, ..) => t,
&Self::ConstPrim(t, ..) => t,
&Self::Term(t, ..) => t, &Self::Term(t, ..) => t,
&Self::Wildcard(t, ..) => t, &Self::Wildcard(t, ..) => t,
&Self::And(t, ..) => t, &Self::And(t, ..) => t,
@@ -284,6 +288,7 @@ impl Expr {
&Self::Term(t, ..) => t, &Self::Term(t, ..) => t,
&Self::Var(t, ..) => t, &Self::Var(t, ..) => t,
&Self::ConstInt(t, ..) => t, &Self::ConstInt(t, ..) => t,
&Self::ConstPrim(t, ..) => t,
&Self::Let(t, ..) => t, &Self::Let(t, ..) => t,
} }
} }
@@ -297,6 +302,7 @@ impl TypeEnv {
sym_map: HashMap::new(), sym_map: HashMap::new(),
types: vec![], types: vec![],
type_map: HashMap::new(), type_map: HashMap::new(),
const_types: HashMap::new(),
errors: vec![], errors: vec![],
}; };
@@ -340,6 +346,29 @@ impl TypeEnv {
} }
} }
// Now collect types for extern constants.
for def in &defs.defs {
match def {
&ast::Def::Extern(ast::Extern::Const {
ref name,
ref ty,
pos,
}) => {
let ty = tyenv.intern_mut(ty);
let ty = match tyenv.type_map.get(&ty) {
Some(ty) => *ty,
None => {
tyenv.report_error(pos, "Unknown type for constant".into());
continue;
}
};
let name = tyenv.intern_mut(name);
tyenv.const_types.insert(name, ty);
}
_ => {}
}
}
tyenv.return_errors()?; tyenv.return_errors()?;
Ok(tyenv) Ok(tyenv)
@@ -674,20 +703,15 @@ impl TermEnv {
vars: vec![], vars: vec![],
}; };
let (lhs, ty) = match self.translate_pattern( let (lhs, ty) =
tyenv, match self.translate_pattern(tyenv, &rule.pattern, None, &mut bindings) {
&rule.pattern,
None,
&mut bindings,
) {
Some(x) => x, Some(x) => x,
None => { None => {
// Keep going to collect more errors. // Keep going to collect more errors.
continue; continue;
} }
}; };
let rhs = let rhs = match self.translate_expr(tyenv, &rule.expr, ty, &mut bindings) {
match self.translate_expr(tyenv, &rule.expr, ty, &mut bindings) {
Some(x) => x, Some(x) => x,
None => { None => {
continue; continue;
@@ -814,6 +838,20 @@ impl TermEnv {
}; };
Some((Pattern::ConstInt(ty, val), ty)) Some((Pattern::ConstInt(ty, val), ty))
} }
&ast::Pattern::ConstPrim { ref val, pos } => {
let val = tyenv.intern_mut(val);
let const_ty = match tyenv.const_types.get(&val) {
Some(ty) => *ty,
None => {
tyenv.report_error(pos, "Unknown constant".into());
return None;
}
};
if expected_ty.is_some() && expected_ty != Some(const_ty) {
tyenv.report_error(pos, "Type mismatch for constant".into());
}
Some((Pattern::ConstPrim(const_ty, val), const_ty))
}
&ast::Pattern::Wildcard { pos } => { &ast::Pattern::Wildcard { pos } => {
let ty = match expected_ty { let ty = match expected_ty {
Some(t) => t, Some(t) => t,
@@ -1045,8 +1083,7 @@ impl TermEnv {
) -> Option<(TermArgPattern, TypeId)> { ) -> Option<(TermArgPattern, TypeId)> {
match pat { match pat {
&ast::TermArgPattern::Pattern(ref pat) => { &ast::TermArgPattern::Pattern(ref pat) => {
let (subpat, ty) = let (subpat, ty) = self.translate_pattern(tyenv, pat, expected_ty, bindings)?;
self.translate_pattern(tyenv, pat, expected_ty, bindings)?;
Some((TermArgPattern::Pattern(subpat), ty)) Some((TermArgPattern::Pattern(subpat), ty))
} }
&ast::TermArgPattern::Expr(ref expr) => { &ast::TermArgPattern::Expr(ref expr) => {
@@ -1152,6 +1189,29 @@ impl TermEnv {
Some(Expr::Var(bv.ty, bv.id)) Some(Expr::Var(bv.ty, bv.id))
} }
&ast::Expr::ConstInt { val, .. } => Some(Expr::ConstInt(ty, val)), &ast::Expr::ConstInt { val, .. } => Some(Expr::ConstInt(ty, val)),
&ast::Expr::ConstPrim { ref val, pos } => {
let val = tyenv.intern_mut(val);
let const_ty = match tyenv.const_types.get(&val) {
Some(ty) => *ty,
None => {
tyenv.report_error(pos, "Unknown constant".into());
return None;
}
};
if const_ty != ty {
tyenv.report_error(
pos,
format!(
"Constant '{}' has wrong type: expected {}, but is actually {}",
tyenv.syms[val.index()],
tyenv.types[ty.index()].name(tyenv),
tyenv.types[const_ty.index()].name(tyenv)
),
);
return None;
}
Some(Expr::ConstPrim(ty, val))
}
&ast::Expr::Let { &ast::Expr::Let {
ref defs, ref defs,
ref body, ref body,
@@ -1191,15 +1251,13 @@ impl TermEnv {
}; };
// Evaluate the variable's value. // Evaluate the variable's value.
let val = Box::new( let val = Box::new(match self.translate_expr(tyenv, &def.val, ty, bindings) {
match self.translate_expr(tyenv, &def.val, ty, bindings) {
Some(e) => e, Some(e) => e,
None => { None => {
// Keep going for more errors. // Keep going for more errors.
continue; continue;
} }
}, });
);
// Bind the var with the given type. // Bind the var with the given type.
let id = VarId(bindings.next_var); let id = VarId(bindings.next_var);
@@ -1240,14 +1298,30 @@ mod test {
.expect("should parse"); .expect("should parse");
let tyenv = TypeEnv::from_ast(&ast).expect("should not have type-definition errors"); let tyenv = TypeEnv::from_ast(&ast).expect("should not have type-definition errors");
let sym_a = tyenv.intern(&Ident("A".to_string())).unwrap(); let sym_a = tyenv
let sym_b = tyenv.intern(&Ident("B".to_string())).unwrap(); .intern(&Ident("A".to_string(), Default::default()))
let sym_c = tyenv.intern(&Ident("C".to_string())).unwrap(); .unwrap();
let sym_a_b = tyenv.intern(&Ident("A.B".to_string())).unwrap(); let sym_b = tyenv
let sym_a_c = tyenv.intern(&Ident("A.C".to_string())).unwrap(); .intern(&Ident("B".to_string(), Default::default()))
let sym_u32 = tyenv.intern(&Ident("u32".to_string())).unwrap(); .unwrap();
let sym_f1 = tyenv.intern(&Ident("f1".to_string())).unwrap(); let sym_c = tyenv
let sym_f2 = tyenv.intern(&Ident("f2".to_string())).unwrap(); .intern(&Ident("C".to_string(), Default::default()))
.unwrap();
let sym_a_b = tyenv
.intern(&Ident("A.B".to_string(), Default::default()))
.unwrap();
let sym_a_c = tyenv
.intern(&Ident("A.C".to_string(), Default::default()))
.unwrap();
let sym_u32 = tyenv
.intern(&Ident("u32".to_string(), Default::default()))
.unwrap();
let sym_f1 = tyenv
.intern(&Ident("f1".to_string(), Default::default()))
.unwrap();
let sym_f2 = tyenv
.intern(&Ident("f2".to_string(), Default::default()))
.unwrap();
assert_eq!(tyenv.type_map.get(&sym_u32).unwrap(), &TypeId(0)); assert_eq!(tyenv.type_map.get(&sym_u32).unwrap(), &TypeId(0));
assert_eq!(tyenv.type_map.get(&sym_a).unwrap(), &TypeId(1)); assert_eq!(tyenv.type_map.get(&sym_a).unwrap(), &TypeId(1));

View File

@@ -7,6 +7,9 @@
(extern extractor Ext1 ext1) (extern extractor Ext1 ext1)
(extern extractor Ext2 ext2) (extern extractor Ext2 ext2)
(extern const $A u32)
(extern const $B u32)
(decl C (bool) A) (decl C (bool) A)
(extern constructor C c) (extern constructor C c)
@@ -32,3 +35,8 @@
(rule (rule
(Lower2 (Opcode.C)) (Lower2 (Opcode.C))
(MachInst.F)) (MachInst.F))
(decl F (Opcode) u32)
(rule
(F _)
$B)