Skip to content

Commit ba5beb6

Browse files
authored
fix: Error on infinitely recursive types (#7579)
1 parent b4916a5 commit ba5beb6

File tree

6 files changed

+200
-77
lines changed

6 files changed

+200
-77
lines changed

compiler/noirc_frontend/src/elaborator/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1893,6 +1893,7 @@ impl<'context> Elaborator<'context> {
18931893

18941894
datatype.borrow_mut().init_variants();
18951895
let module_id = ModuleId { krate: self.crate_id, local_id: typ.module_id };
1896+
self.resolving_ids.insert(*type_id);
18961897

18971898
for (i, variant) in typ.enum_def.variants.iter().enumerate() {
18981899
let parameters = variant.item.parameters.as_ref();
@@ -1919,7 +1920,10 @@ impl<'context> Elaborator<'context> {
19191920
let location = variant.item.name.location();
19201921
self.interner.add_definition_location(reference_id, location, Some(module_id));
19211922
}
1923+
1924+
self.resolving_ids.remove(type_id);
19221925
}
1926+
self.generics.clear();
19231927
}
19241928

19251929
fn elaborate_global(&mut self, global: UnresolvedGlobal) {

compiler/noirc_frontend/src/monomorphization/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub enum MonomorphizationError {
1616
ComptimeTypeInRuntimeCode { typ: String, location: Location },
1717
CheckedTransmuteFailed { actual: Type, expected: Type, location: Location },
1818
CheckedCastFailed { actual: Type, expected: Type, location: Location },
19+
RecursiveType { typ: Type, location: Location },
1920
}
2021

2122
impl MonomorphizationError {
@@ -28,6 +29,7 @@ impl MonomorphizationError {
2829
| MonomorphizationError::ComptimeTypeInRuntimeCode { location, .. }
2930
| MonomorphizationError::CheckedTransmuteFailed { location, .. }
3031
| MonomorphizationError::CheckedCastFailed { location, .. }
32+
| MonomorphizationError::RecursiveType { location, .. }
3133
| MonomorphizationError::NoDefaultType { location, .. } => *location,
3234
MonomorphizationError::InterpreterError(error) => error.location(),
3335
}
@@ -67,6 +69,11 @@ impl From<MonomorphizationError> for CustomDiagnostic {
6769
let secondary = "Comptime type used here".into();
6870
return CustomDiagnostic::simple_error(message, secondary, *location);
6971
}
72+
MonomorphizationError::RecursiveType { typ, location } => {
73+
let message = format!("Type `{typ}` is recursive");
74+
let secondary = "All types in Noir must have a known size at compile-time".into();
75+
return CustomDiagnostic::simple_error(message, secondary, *location);
76+
}
7077
};
7178

7279
let location = error.location();

compiler/noirc_frontend/src/monomorphization/mod.rs

+56-19
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::{
2727
};
2828
use acvm::{FieldElement, acir::AcirField};
2929
use ast::{GlobalId, While};
30-
use fxhash::FxHashMap as HashMap;
30+
use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet};
3131
use iter_extended::{btree_map, try_vecmap, vecmap};
3232
use noirc_errors::Location;
3333
use noirc_printable_type::PrintableType;
@@ -48,6 +48,7 @@ mod debug;
4848
pub mod debug_types;
4949
pub mod errors;
5050
pub mod printer;
51+
pub mod tests;
5152

5253
struct LambdaContext {
5354
env_ident: ast::Ident,
@@ -378,7 +379,10 @@ impl<'interner> Monomorphizer<'interner> {
378379
other => other,
379380
};
380381

381-
let return_type = Self::convert_type(return_type, meta.location)?;
382+
// If `convert_type` fails here it is most likely because of generics at the
383+
// call site after instantiating this function's type. So show the error there
384+
// instead of at the function definition.
385+
let return_type = Self::convert_type(return_type, location)?;
382386
let unconstrained = self.in_unconstrained_function;
383387

384388
let attributes = self.interner.function_attributes(&f);
@@ -1120,6 +1124,14 @@ impl<'interner> Monomorphizer<'interner> {
11201124

11211125
/// Convert a non-tuple/struct type to a monomorphized type
11221126
fn convert_type(typ: &HirType, location: Location) -> Result<ast::Type, MonomorphizationError> {
1127+
Self::convert_type_helper(typ, location, &mut HashSet::default())
1128+
}
1129+
1130+
fn convert_type_helper(
1131+
typ: &HirType,
1132+
location: Location,
1133+
seen_types: &mut HashSet<Type>,
1134+
) -> Result<ast::Type, MonomorphizationError> {
11231135
let typ = typ.follow_bindings_shallow();
11241136
Ok(match typ.as_ref() {
11251137
HirType::FieldElement => ast::Type::Field,
@@ -1155,12 +1167,14 @@ impl<'interner> Monomorphizer<'interner> {
11551167
});
11561168
}
11571169
};
1158-
let fields = Box::new(Self::convert_type(fields.as_ref(), location)?);
1170+
let fields =
1171+
Box::new(Self::convert_type_helper(fields.as_ref(), location, seen_types)?);
11591172
ast::Type::FmtString(size, fields)
11601173
}
11611174
HirType::Unit => ast::Type::Unit,
11621175
HirType::Array(length, element) => {
1163-
let element = Box::new(Self::convert_type(element.as_ref(), location)?);
1176+
let element =
1177+
Box::new(Self::convert_type_helper(element.as_ref(), location, seen_types)?);
11641178
let length = match length.evaluate_to_u32(location) {
11651179
Ok(length) => length,
11661180
Err(err) => {
@@ -1175,15 +1189,16 @@ impl<'interner> Monomorphizer<'interner> {
11751189
ast::Type::Array(length, element)
11761190
}
11771191
HirType::Slice(element) => {
1178-
let element = Box::new(Self::convert_type(element.as_ref(), location)?);
1192+
let element =
1193+
Box::new(Self::convert_type_helper(element.as_ref(), location, seen_types)?);
11791194
ast::Type::Slice(element)
11801195
}
11811196
HirType::TraitAsType(..) => {
11821197
unreachable!("All TraitAsType should be replaced before calling convert_type");
11831198
}
11841199
HirType::NamedGeneric(binding, _) => {
11851200
if let TypeBinding::Bound(binding) = &*binding.borrow() {
1186-
return Self::convert_type(binding, location);
1201+
return Self::convert_type_helper(binding, location, seen_types);
11871202
}
11881203

11891204
// Default any remaining unbound type variables.
@@ -1195,13 +1210,21 @@ impl<'interner> Monomorphizer<'interner> {
11951210

11961211
HirType::CheckedCast { from, to } => {
11971212
Self::check_checked_cast(from, to, location)?;
1198-
Self::convert_type(to, location)?
1213+
Self::convert_type_helper(to, location, seen_types)?
11991214
}
12001215

12011216
HirType::TypeVariable(binding) => {
1217+
let input_type = typ.as_ref().clone();
1218+
if !seen_types.insert(input_type.clone()) {
1219+
let typ = input_type;
1220+
return Err(MonomorphizationError::RecursiveType { typ, location });
1221+
}
1222+
12021223
let type_var_kind = match &*binding.borrow() {
12031224
TypeBinding::Bound(binding) => {
1204-
return Self::convert_type(binding, location);
1225+
let typ = Self::convert_type_helper(binding, location, seen_types);
1226+
seen_types.remove(&input_type);
1227+
return typ;
12051228
}
12061229
TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(),
12071230
};
@@ -1214,7 +1237,8 @@ impl<'interner> Monomorphizer<'interner> {
12141237
None => return Err(MonomorphizationError::NoDefaultType { location }),
12151238
};
12161239

1217-
let monomorphized_default = Self::convert_type(&default, location)?;
1240+
let monomorphized_default =
1241+
Self::convert_type_helper(&default, location, seen_types)?;
12181242
binding.bind(default);
12191243
monomorphized_default
12201244
}
@@ -1226,19 +1250,30 @@ impl<'interner> Monomorphizer<'interner> {
12261250
Self::check_type(arg, location)?;
12271251
}
12281252

1253+
let input_type = typ.as_ref().clone();
1254+
if !seen_types.insert(input_type.clone()) {
1255+
let typ = input_type;
1256+
return Err(MonomorphizationError::RecursiveType { typ, location });
1257+
}
1258+
12291259
let def = def.borrow();
12301260
if let Some(fields) = def.get_fields(args) {
1231-
let fields =
1232-
try_vecmap(fields, |(_, field)| Self::convert_type(&field, location))?;
1261+
let fields = try_vecmap(fields, |(_, field)| {
1262+
Self::convert_type_helper(&field, location, seen_types)
1263+
})?;
1264+
1265+
seen_types.remove(&input_type);
12331266
ast::Type::Tuple(fields)
12341267
} else if let Some(variants) = def.get_variants(args) {
12351268
// Enums are represented as (tag, variant1, variant2, .., variantN)
12361269
let mut fields = vec![ast::Type::Field];
12371270
for (_, variant_fields) in variants {
1238-
let variant_fields =
1239-
try_vecmap(variant_fields, |typ| Self::convert_type(&typ, location))?;
1271+
let variant_fields = try_vecmap(variant_fields, |typ| {
1272+
Self::convert_type_helper(&typ, location, seen_types)
1273+
})?;
12401274
fields.push(ast::Type::Tuple(variant_fields));
12411275
}
1276+
seen_types.remove(&input_type);
12421277
ast::Type::Tuple(fields)
12431278
} else {
12441279
unreachable!("Data type has no body")
@@ -1252,18 +1287,20 @@ impl<'interner> Monomorphizer<'interner> {
12521287
Self::check_type(arg, location)?;
12531288
}
12541289

1255-
Self::convert_type(&def.borrow().get_type(args), location)?
1290+
Self::convert_type_helper(&def.borrow().get_type(args), location, seen_types)?
12561291
}
12571292

12581293
HirType::Tuple(fields) => {
1259-
let fields = try_vecmap(fields, |x| Self::convert_type(x, location))?;
1294+
let fields =
1295+
try_vecmap(fields, |x| Self::convert_type_helper(x, location, seen_types))?;
12601296
ast::Type::Tuple(fields)
12611297
}
12621298

12631299
HirType::Function(args, ret, env, unconstrained) => {
1264-
let args = try_vecmap(args, |x| Self::convert_type(x, location))?;
1265-
let ret = Box::new(Self::convert_type(ret, location)?);
1266-
let env = Self::convert_type(env, location)?;
1300+
let args =
1301+
try_vecmap(args, |x| Self::convert_type_helper(x, location, seen_types))?;
1302+
let ret = Box::new(Self::convert_type_helper(ret, location, seen_types)?);
1303+
let env = Self::convert_type_helper(env, location, seen_types)?;
12671304
match &env {
12681305
ast::Type::Unit => {
12691306
ast::Type::Function(args, ret, Box::new(env), *unconstrained)
@@ -1282,7 +1319,7 @@ impl<'interner> Monomorphizer<'interner> {
12821319

12831320
// Lower both mutable & immutable references to the same reference type
12841321
HirType::Reference(element, _mutable) => {
1285-
let element = Self::convert_type(element, location)?;
1322+
let element = Self::convert_type_helper(element, location, seen_types)?;
12861323
ast::Type::MutableReference(Box::new(element))
12871324
}
12881325

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#![cfg(test)]
2+
use crate::tests::get_program;
3+
4+
use super::{ast::Program, errors::MonomorphizationError, monomorphize};
5+
6+
pub fn get_monomorphized(src: &str) -> Result<Program, MonomorphizationError> {
7+
let (_parsed_module, mut context, errors) = get_program(src);
8+
assert!(
9+
errors.iter().all(|err| !err.is_error()),
10+
"Expected monomorphized program to have no errors before monomorphization, but found: {errors:?}"
11+
);
12+
13+
let main = context
14+
.get_main_function(context.root_crate_id())
15+
.unwrap_or_else(|| panic!("get_monomorphized: test program contains no 'main' function"));
16+
17+
monomorphize(main, &mut context.def_interner, false)
18+
}
19+
20+
fn check_rewrite(src: &str, expected: &str) {
21+
let program = get_monomorphized(src).unwrap();
22+
assert!(format!("{}", program) == expected);
23+
}
24+
25+
#[test]
26+
fn bounded_recursive_type_errors() {
27+
// We want to eventually allow bounded recursive types like this, but for now they are
28+
// disallowed because they cause a panic in convert_type during monomorphization.
29+
let src = "
30+
fn main() {
31+
let _tree: Tree<Tree<Tree<()>>> = Tree::Branch(
32+
Tree::Branch(Tree::Leaf, Tree::Leaf),
33+
Tree::Branch(Tree::Leaf, Tree::Leaf),
34+
);
35+
}
36+
37+
enum Tree<T> {
38+
Branch(T, T),
39+
Leaf,
40+
}";
41+
42+
let error = get_monomorphized(src).unwrap_err();
43+
assert!(matches!(error, MonomorphizationError::RecursiveType { .. }));
44+
}
45+
46+
#[test]
47+
fn recursive_type_with_alias_errors() {
48+
// We want to eventually allow bounded recursive types like this, but for now they are
49+
// disallowed because they cause a panic in convert_type during monomorphization.
50+
//
51+
// In the future we could lower this type to:
52+
// struct OptOptUnit {
53+
// is_some: Field,
54+
// some: OptUnit,
55+
// none: (),
56+
// }
57+
//
58+
// struct OptUnit {
59+
// is_some: Field,
60+
// some: (),
61+
// none: (),
62+
// }
63+
let src = "
64+
fn main() {
65+
let _tree: Opt<OptAlias<()>> = Opt::Some(OptAlias::None);
66+
}
67+
68+
type OptAlias<T> = Opt<T>;
69+
70+
enum Opt<T> {
71+
Some(T),
72+
None,
73+
}";
74+
75+
let error = get_monomorphized(src).unwrap_err();
76+
assert!(matches!(error, MonomorphizationError::RecursiveType { .. }));
77+
}
78+
79+
#[test]
80+
fn mutually_recursive_types_error() {
81+
let src = "
82+
fn main() {
83+
let _zero = Even::Zero;
84+
}
85+
86+
enum Even {
87+
Zero,
88+
Succ(Odd),
89+
}
90+
91+
enum Odd {
92+
One,
93+
Succ(Even),
94+
}";
95+
96+
let error = get_monomorphized(src).unwrap_err();
97+
assert!(matches!(error, MonomorphizationError::RecursiveType { .. }));
98+
}
99+
100+
#[test]
101+
fn simple_closure_with_no_captured_variables() {
102+
let src = r#"
103+
fn main() -> pub Field {
104+
let x = 1;
105+
let closure = || x;
106+
closure()
107+
}
108+
"#;
109+
110+
let expected_rewrite = r#"fn main$f0() -> Field {
111+
let x$0 = 1;
112+
let closure$3 = {
113+
let closure_variable$2 = {
114+
let env$1 = (x$l0);
115+
(env$l1, lambda$f1)
116+
};
117+
closure_variable$l2
118+
};
119+
{
120+
let tmp$4 = closure$l3;
121+
tmp$l4.1(tmp$l4.0)
122+
}
123+
}
124+
fn lambda$f1(mut env$l1: (Field)) -> Field {
125+
env$l1.0
126+
}
127+
"#;
128+
check_rewrite(src, expected_rewrite);
129+
}

0 commit comments

Comments
 (0)