Skip to content

Commit 1da8d86

Browse files
authored
isle tuple fields for structs and enum variants (#13335)
1 parent 197bdad commit 1da8d86

8 files changed

Lines changed: 379 additions & 96 deletions

File tree

cranelift/isle/docs/language-reference.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,12 +1491,18 @@ The grammar accepted by the parser is as follows:
14911491
14921492
<type-body> ::= "(" "primitive" <ident> ")"
14931493
| "(" "enum" <enum-variant>* ")"
1494-
| "(" "struct" <variant-field>* ")"
1494+
| "(" "struct" <fields> ")"
14951495
14961496
<enum-variant> ::= <ident>
1497-
| "(" <ident> <variant-field>* ")"
1497+
| "(" <ident> <fields> ")"
14981498
1499-
<variant-field> ::= "(" <ident> <ty> ")"
1499+
<fields> ::= <struct-fields> | <tuple-fields>
1500+
1501+
<struct-fields> ::= <struct-field>*
1502+
<struct-field> ::= "(" <ident> <ty> ")"
1503+
1504+
<tuple-fields> ::= <tuple-field>*
1505+
<tuple-field> ::= <ty>
15001506
15011507
<ty> ::= <ident>
15021508
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
(type A (struct u32 u32))
2+
(type B (enum
3+
(Ba (x u32))
4+
(Bb u32 u32 u32)))
5+
(type D (enum
6+
(Da A)
7+
(Db B)
8+
(Dc)))
9+
10+
(type UnitStruct (struct))
11+
(type UninhabitedEnum (enum))
12+
13+
(decl lower (D) B)
14+
15+
(rule 0
16+
(lower (D.Da (A x _)))
17+
(B.Ba x))
18+
19+
(rule 0
20+
(lower (D.Db (B.Ba x)))
21+
(B.Bb x x x))
22+
23+
(rule 0
24+
(lower (D.Db (B.Bb _ _ x)))
25+
(B.Ba x))
26+
27+
(rule 0
28+
(lower (D.Dc))
29+
(B.Ba 42))
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
extern crate alloc;
2+
extern crate core;
3+
4+
mod tuple_variants;
5+
use tuple_variants::*;
6+
7+
struct Context;
8+
impl tuple_variants::Context for Context {}
9+
10+
fn main() {
11+
assert!(matches!(
12+
constructor_lower(&mut Context, &D::Da(A(1, 2))),
13+
B::Ba { x: 1 }
14+
));
15+
assert!(matches!(
16+
constructor_lower(&mut Context, &D::Db(B::Ba { x: 6 })),
17+
B::Bb(6, 6, 6)
18+
));
19+
assert!(matches!(
20+
constructor_lower(&mut Context, &D::Db(B::Bb(1, 2, 3))),
21+
B::Ba { x: 3 }
22+
));
23+
assert!(matches!(
24+
constructor_lower(&mut Context, &D::Dc),
25+
B::Ba { x: 42 }
26+
));
27+
28+
let _ = UnitStruct;
29+
assert_eq!(size_of::<UnitStruct>(), 0);
30+
assert_eq!(size_of::<UninhabitedEnum>(), 0);
31+
}

cranelift/isle/isle/src/ast.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,55 @@ pub struct Type {
4646
pub enum TypeValue {
4747
Primitive(Ident, Pos),
4848
Enum(Vec<Variant>, Pos),
49-
Struct(Vec<Field>, Pos),
49+
Struct(Fields, Pos),
5050
}
5151

5252
/// One variant of an enum type.
5353
#[derive(Clone, PartialEq, Eq, Debug)]
5454
pub struct Variant {
5555
pub name: Ident,
56-
pub fields: Vec<Field>,
56+
pub fields: Fields,
5757
pub pos: Pos,
5858
}
5959

60-
/// One field of an enum variant.
60+
/// The fields of a struct or enum variant, formatted as a struct or tuple.
6161
#[derive(Clone, PartialEq, Eq, Debug)]
62-
pub struct Field {
62+
pub enum Fields {
63+
Unit,
64+
Struct(StructFields),
65+
Tuple(TupleFields),
66+
}
67+
68+
/// A List of named fields of a struct.
69+
#[derive(Clone, PartialEq, Eq, Debug)]
70+
pub struct StructFields {
71+
pub fields: Vec<StructField>,
72+
pub pos: Pos,
73+
}
74+
75+
/// One named field of a struct or enum variant.
76+
#[derive(Clone, PartialEq, Eq, Debug)]
77+
pub struct StructField {
6378
pub name: Ident,
6479
pub ty: Ident,
6580
pub pos: Pos,
6681
}
6782

83+
/// A List of unnamed fields of a tuple.
84+
#[derive(Clone, PartialEq, Eq, Debug)]
85+
pub struct TupleFields {
86+
pub fields: Vec<TupleField>,
87+
pub pos: Pos,
88+
}
89+
90+
/// One unnamed field of a tuple.
91+
#[derive(Clone, PartialEq, Eq, Debug)]
92+
pub struct TupleField {
93+
pub index: usize,
94+
pub ty: Ident,
95+
pub pos: Pos,
96+
}
97+
6898
/// A declaration of a term with its argument and return types.
6999
#[derive(Clone, PartialEq, Eq, Debug)]
70100
pub struct Decl {

cranelift/isle/isle/src/codegen.rs

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
33
use crate::files::Files;
44
use crate::sema::{
5-
BuiltinType, ExternalSig, Field, IntType, ReturnKind, Term, TermEnv, TermId, Type, TypeEnv,
5+
BuiltinType, ExternalSig, Fields, IntType, ReturnKind, Term, TermEnv, TermId, Type, TypeEnv,
66
TypeId,
77
};
88
use crate::serialize::{Block, ControlFlow, EvalStep, MatchArm};
99
use crate::stablemapset::StableSet;
1010
use crate::trie_again::{Binding, BindingId, Constraint, RuleSet};
11+
use std::borrow::Cow;
1112
use std::fmt::Write;
1213
use std::slice::Iter;
1314
use std::sync::Arc;
@@ -412,7 +413,7 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
412413

413414
// Generate the `derive`s.
414415
let debug_derive = if is_nodebug { "" } else { ", Debug" };
415-
if variants.iter().all(|v| v.fields.is_empty()) {
416+
if variants.iter().all(|v| v.fields == Fields::Unit) {
416417
writeln!(code, "#[derive(Copy, Clone, PartialEq, Eq{debug_derive})]")
417418
.unwrap();
418419
} else {
@@ -422,18 +423,10 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
422423
writeln!(code, "pub enum {name} {{").unwrap();
423424
for variant in variants {
424425
let name = &self.typeenv.syms[variant.name.index()];
425-
if variant.fields.is_empty() {
426-
writeln!(code, " {name},").unwrap();
427-
} else {
428-
writeln!(code, " {name} {{").unwrap();
429-
for field in &variant.fields {
430-
let name = &self.typeenv.syms[field.name.index()];
431-
let ty_name =
432-
self.typeenv.types[field.ty.index()].name(self.typeenv);
433-
writeln!(code, " {name}: {ty_name},").unwrap();
434-
}
435-
writeln!(code, " }},").unwrap();
436-
}
426+
write!(code, " {name}").unwrap();
427+
self.generate_fields(&mut *code, &variant.fields, " ", false)
428+
.unwrap();
429+
writeln!(code, ",").unwrap();
437430
}
438431
writeln!(code, "}}").unwrap();
439432
}
@@ -456,26 +449,60 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
456449

457450
// Generate the `derive`s.
458451
let debug_derive = if is_nodebug { "" } else { ", Debug" };
459-
if fields.is_empty() {
460-
writeln!(code, "#[derive(Copy, Clone, PartialEq, Eq{debug_derive})]")
461-
.unwrap();
462-
writeln!(code, "pub struct {name};").unwrap();
463-
} else {
464-
writeln!(code, "#[derive(Clone{debug_derive})]").unwrap();
465-
writeln!(code, "pub struct {name} {{").unwrap();
466-
for field in fields {
467-
let name = &self.typeenv.syms[field.name.index()];
468-
let ty_name = self.typeenv.types[field.ty.index()].name(self.typeenv);
469-
writeln!(code, " pub {name}: {ty_name},").unwrap();
452+
match fields {
453+
Fields::Unit => {
454+
writeln!(code, "#[derive(Copy, Clone, PartialEq, Eq{debug_derive})]")
455+
.unwrap();
456+
writeln!(code, "pub struct {name};").unwrap();
457+
}
458+
Fields::Tuple(_) | Fields::Struct(_) => {
459+
writeln!(code, "#[derive(Clone{debug_derive})]").unwrap();
460+
write!(code, "pub struct {name}").unwrap();
461+
self.generate_fields(&mut *code, &fields, "", true).unwrap();
462+
if matches!(fields, Fields::Tuple(_)) {
463+
write!(code, ";").unwrap();
464+
}
470465
}
471-
writeln!(code, "}}").unwrap();
472466
}
473467
}
474468
_ => {}
475469
}
476470
}
477471
}
478472

473+
fn generate_fields(
474+
&self,
475+
code: &mut String,
476+
fields: &Fields,
477+
pad: &str,
478+
allow_pub: bool,
479+
) -> std::fmt::Result {
480+
let _pub = if allow_pub { "pub " } else { "" };
481+
match fields {
482+
Fields::Unit => Ok(()),
483+
Fields::Struct(fields) => {
484+
writeln!(code, " {{")?;
485+
for field in &fields.fields {
486+
let name = &self.typeenv.syms[field.name.index()];
487+
let ty_name = self.typeenv.types[field.ty.index()].name(self.typeenv);
488+
writeln!(code, "{pad} {_pub}{name}: {ty_name},")?;
489+
}
490+
write!(code, "{pad}}}")
491+
}
492+
Fields::Tuple(fields) => {
493+
write!(code, "(")?;
494+
for (i, field) in fields.fields.iter().enumerate() {
495+
let ty_name = self.typeenv.types[field.ty.index()].name(self.typeenv);
496+
write!(code, "{_pub}{ty_name}")?;
497+
if i < fields.fields.len() - 1 {
498+
write!(code, ", ")?;
499+
}
500+
}
501+
write!(code, ")")
502+
}
503+
}
504+
}
505+
479506
fn type_name(&self, typeid: TypeId, by_ref: bool) -> String {
480507
match self.typeenv.types[typeid.index()] {
481508
Type::Builtin(bt) => String::from(bt.name()),
@@ -926,17 +953,19 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
926953

927954
let extract_fields = |ctx: &mut BodyContext<W>,
928955
field_bindings: &[BindingId],
929-
type_fields: &[Field]|
956+
fields: &Fields|
930957
-> std::fmt::Result {
931958
if !field_bindings.is_empty() {
932959
ctx.begin_block()?;
933-
for (field, value) in type_fields.iter().zip(field_bindings.iter()) {
934-
write!(
935-
ctx.out,
936-
"{}{}: ",
937-
&ctx.indent,
938-
&self.typeenv.syms[field.name.index()],
939-
)?;
960+
for (i, value) in field_bindings.iter().enumerate() {
961+
let field_name = match fields {
962+
Fields::Unit => panic!(),
963+
Fields::Struct(fields) => {
964+
Cow::Borrowed(&self.typeenv.syms[fields.fields[i].name.index()])
965+
}
966+
Fields::Tuple(_) => Cow::Owned(format!("{i}")),
967+
};
968+
write!(ctx.out, "{}{field_name}: ", &ctx.indent)?;
940969
self.emit_expr(ctx, *value)?;
941970
if ctx.is_ref.contains(value) {
942971
write!(ctx.out, ".clone()")?;
@@ -1095,20 +1124,24 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
10951124
&self,
10961125
ctx: &mut BodyContext<W>,
10971126
bindings: &[Option<BindingId>],
1098-
fields: &[Field],
1127+
fields: &Fields,
10991128
) -> std::fmt::Result {
11001129
if !bindings.is_empty() {
11011130
ctx.begin_block()?;
11021131
let mut skipped_some = false;
1103-
for (&binding, field) in bindings.iter().zip(fields.iter()) {
1132+
for (i, &binding) in bindings.iter().enumerate() {
11041133
if let Some(binding) = binding {
1105-
write!(
1106-
ctx.out,
1107-
"{}{}: ",
1108-
&ctx.indent,
1109-
&self.typeenv.syms[field.name.index()]
1110-
)?;
1111-
let (is_ref, _) = self.ty(field.ty);
1134+
let (field_name, field_ty) = match fields {
1135+
Fields::Unit => panic!(),
1136+
Fields::Struct(fields) => {
1137+
let field = &fields.fields[i];
1138+
let name = &self.typeenv.syms[field.name.index()];
1139+
(Cow::Borrowed(name), field.ty)
1140+
}
1141+
Fields::Tuple(fields) => (Cow::Owned(format!("{i}")), fields.fields[i].ty),
1142+
};
1143+
write!(ctx.out, "{}{field_name}: ", &ctx.indent)?;
1144+
let (is_ref, _) = self.ty(field_ty);
11121145
if is_ref {
11131146
ctx.set_ref(binding, true);
11141147
write!(ctx.out, "ref ")?;

0 commit comments

Comments
 (0)