Skip to content

Commit 3cf2b0c

Browse files
committed
Fix nested macro expansion panic
1 parent 54578cb commit 3cf2b0c

3 files changed

Lines changed: 44 additions & 4 deletions

File tree

src/compiler.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,13 @@ impl Compiler {
554554
// Expand macros in all function bodies.
555555
for decl in &mut decls {
556556
if let Decl::Func(ref mut fdecl) = decl {
557-
fdecl.expand_macros(&macros);
557+
if let Err((loc, msg)) = fdecl.expand_macros(&macros) {
558+
if !self.quiet {
559+
print_error_with_context(loc, &msg);
560+
}
561+
self.last_errors.push(format_error(loc, &msg));
562+
return false;
563+
}
558564
}
559565
}
560566

src/decl.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,25 @@ impl FuncDecl {
9797
}
9898

9999
/// Expand all macro invocations in this function's arena.
100-
pub fn expand_macros(&mut self, macros: &std::collections::HashMap<Name, FuncDecl>) {
101-
let n = self.arena.exprs.len();
102-
for i in 0..n {
100+
pub fn expand_macros(
101+
&mut self,
102+
macros: &std::collections::HashMap<Name, FuncDecl>,
103+
) -> Result<(), (Loc, String)> {
104+
let mut i = 0;
105+
let mut expansion_count = 0;
106+
while i < self.arena.exprs.len() {
103107
if let Expr::Macro(name, ref args) = self.arena.exprs[i] {
108+
let loc = self.arena.locs[i];
104109
let args = args.clone();
105110
if let Some(mac) = macros.get(&name) {
111+
expansion_count += 1;
112+
if expansion_count > 100_000 {
113+
return Err((
114+
loc,
115+
format!("macro expansion limit exceeded while expanding {}", name),
116+
));
117+
}
118+
106119
let subst: Vec<(Name, ExprID)> = mac
107120
.params
108121
.iter()
@@ -115,9 +128,12 @@ impl FuncDecl {
115128

116129
self.arena.exprs[i] = self.arena.exprs[new_body].clone();
117130
self.arena.locs[i] = self.arena.locs[new_body];
131+
continue;
118132
}
119133
}
134+
i += 1;
120135
}
136+
Ok(())
121137
}
122138
}
123139

tests/cases/nested_macro.lyte

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// expected stdout:
2+
// compilation successful
3+
// assert(true)
4+
5+
macro inc(x) {
6+
x = x + 1
7+
}
8+
9+
macro inc_twice(x) {
10+
@inc(x)
11+
@inc(x)
12+
}
13+
14+
main {
15+
var x = 0
16+
@inc_twice(x)
17+
assert(x == 2)
18+
}

0 commit comments

Comments
 (0)