Skip to content

Commit 42de852

Browse files
committed
Check exit/next is inside of the loop
1 parent e2bcb95 commit 42de852

11 files changed

+277
-125
lines changed

vhdl_lang/src/analysis/concurrent.rs

+17-17
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,8 @@ impl<'a> AnalyzeContext<'a> {
2424
diagnostics: &mut dyn DiagnosticHandler,
2525
) -> FatalResult {
2626
for statement in statements.iter_mut() {
27-
let parent = if statement.statement.can_have_label() {
28-
if let Some(id) = statement.label.as_ref().and_then(|label| label.decl) {
29-
self.arena.get(id)
30-
} else {
31-
// Generate an anonymous label if it is not explicitly defined
32-
self.arena.alloc(
33-
Designator::Anonymous(scope.next_anonymous()),
34-
Some(parent),
35-
Related::None,
36-
AnyEntKind::Label,
37-
None,
38-
)
39-
}
27+
let parent = if let Some(id) = statement.label.decl {
28+
self.arena.get(id)
4029
} else {
4130
parent
4231
};
@@ -55,11 +44,22 @@ impl<'a> AnalyzeContext<'a> {
5544
diagnostics: &mut dyn DiagnosticHandler,
5645
) -> FatalResult {
5746
for statement in statements.iter_mut() {
58-
if let Some(ref mut label) = statement.label {
59-
scope.add(
60-
label.define(self.arena, parent, AnyEntKind::Label),
61-
diagnostics,
47+
if let Some(ref mut label) = statement.label.tree {
48+
let ent =
49+
self.arena
50+
.explicit(label.name(), parent, AnyEntKind::Label, Some(label.pos()));
51+
statement.label.decl = Some(ent.id());
52+
scope.add(ent, diagnostics);
53+
} else if statement.statement.can_have_label() {
54+
// Generate an anonymous label if it is not explicitly defined
55+
let ent = self.arena.alloc(
56+
Designator::Anonymous(scope.next_anonymous()),
57+
Some(parent),
58+
Related::None,
59+
AnyEntKind::Label,
60+
None,
6261
);
62+
statement.label.decl = Some(ent.id());
6363
}
6464
}
6565

vhdl_lang/src/analysis/named_entity.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub enum AnyEntKind<'a> {
4444
Type(Type<'a>),
4545
ElementDeclaration(Subtype<'a>),
4646
Label,
47+
LoopLabel,
4748
Object(Object<'a>),
4849
LoopParameter(Option<BaseType<'a>>),
4950
PhysicalLiteral(TypeEnt<'a>),
@@ -101,7 +102,7 @@ impl<'a> AnyEntKind<'a> {
101102
Component(..) => "component",
102103
Attribute(..) => "attribute",
103104
Overloaded(overloaded) => overloaded.describe(),
104-
Label => "label",
105+
Label | LoopLabel => "label",
105106
LoopParameter(_) => "loop parameter",
106107
Object(object) => object.class.describe(),
107108
PhysicalLiteral(..) => "physical literal",

vhdl_lang/src/analysis/names.rs

+2
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ impl<'a> ResolvedName<'a> {
170170
| AnyEntKind::Attribute(_)
171171
| AnyEntKind::ElementDeclaration(_)
172172
| AnyEntKind::Label
173+
| AnyEntKind::LoopLabel
173174
| AnyEntKind::LoopParameter(_) => {
174175
return Err(format!(
175176
"{} cannot be selected from design unit",
@@ -220,6 +221,7 @@ impl<'a> ResolvedName<'a> {
220221
| AnyEntKind::InterfaceFile(_)
221222
| AnyEntKind::Component(_)
222223
| AnyEntKind::Label
224+
| AnyEntKind::LoopLabel
223225
| AnyEntKind::LoopParameter(_)
224226
| AnyEntKind::PhysicalLiteral(_) => ResolvedName::Final(ent),
225227
AnyEntKind::Attribute(_) | AnyEntKind::ElementDeclaration(_) => {

vhdl_lang/src/analysis/package_instance.rs

+1
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ impl<'a> AnalyzeContext<'a> {
332332
AnyEntKind::ElementDeclaration(self.map_subtype(mapping, *subtype)?)
333333
}
334334
AnyEntKind::Label => AnyEntKind::Label,
335+
AnyEntKind::LoopLabel => AnyEntKind::LoopLabel,
335336
AnyEntKind::Object(obj) => AnyEntKind::Object(self.map_object(mapping, obj)?),
336337
AnyEntKind::LoopParameter(typ) => AnyEntKind::LoopParameter(
337338
typ.map(|typ| self.map_type_ent(mapping, typ.into()).base()),

vhdl_lang/src/analysis/sequential.rs

+75-25
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,38 @@ impl<'a> AnalyzeContext<'a> {
2424
diagnostics: &mut dyn DiagnosticHandler,
2525
) -> FatalResult {
2626
for statement in statements.iter_mut() {
27-
if let Some(ref mut label) = statement.label {
28-
scope.add(
29-
label.define(self.arena, parent, AnyEntKind::Label),
30-
diagnostics,
27+
let parent = if let Some(ref mut label) = statement.label.tree {
28+
let ent = self.arena.explicit(
29+
label.name(),
30+
parent,
31+
if statement.statement.is_loop() {
32+
AnyEntKind::LoopLabel
33+
} else {
34+
AnyEntKind::Label
35+
},
36+
Some(label.pos()),
3137
);
32-
}
38+
statement.label.decl = Some(ent.id());
39+
scope.add(ent, diagnostics);
40+
ent
41+
} else if statement.statement.can_have_label() {
42+
// Generate an anonymous label if it is not explicitly defined
43+
let ent = self.arena.alloc(
44+
Designator::Anonymous(scope.next_anonymous()),
45+
Some(parent),
46+
Related::None,
47+
if statement.statement.is_loop() {
48+
AnyEntKind::LoopLabel
49+
} else {
50+
AnyEntKind::Label
51+
},
52+
None,
53+
);
54+
statement.label.decl = Some(ent.id());
55+
ent
56+
} else {
57+
parent
58+
};
3359

3460
match statement.statement {
3561
SequentialStatement::If(ref mut ifstmt) => {
@@ -153,8 +179,10 @@ impl<'a> AnalyzeContext<'a> {
153179
loop_label,
154180
} = &mut exit_stmt.item;
155181

156-
if let Some(ref mut loop_label) = loop_label {
157-
self.check_loop_label(scope, loop_label, diagnostics);
182+
if let Some(loop_label) = loop_label {
183+
self.check_loop_label(scope, parent, loop_label, diagnostics);
184+
} else if !find_outer_loop(parent, None) {
185+
diagnostics.error(&exit_stmt.pos, "Exit can only be used inside a loop")
158186
}
159187

160188
if let Some(expr) = condition {
@@ -167,8 +195,10 @@ impl<'a> AnalyzeContext<'a> {
167195
loop_label,
168196
} = &mut next_stmt.item;
169197

170-
if let Some(ref mut loop_label) = loop_label {
171-
self.check_loop_label(scope, loop_label, diagnostics);
198+
if let Some(loop_label) = loop_label {
199+
self.check_loop_label(scope, parent, loop_label, diagnostics);
200+
} else if !find_outer_loop(parent, None) {
201+
diagnostics.error(&next_stmt.pos, "Next can only be used inside a loop")
172202
}
173203

174204
if let Some(expr) = condition {
@@ -284,6 +314,7 @@ impl<'a> AnalyzeContext<'a> {
284314
fn check_loop_label(
285315
&self,
286316
scope: &Scope<'a>,
317+
parent: EntRef<'a>,
287318
label: &mut WithRef<Ident>,
288319
diagnostics: &mut dyn DiagnosticHandler,
289320
) {
@@ -293,8 +324,14 @@ impl<'a> AnalyzeContext<'a> {
293324
) {
294325
Ok(NamedEntities::Single(ent)) => {
295326
label.set_unique_reference(ent);
296-
if !matches!(ent.kind(), AnyEntKind::Label) {
297-
// @TODO check that is actually a loop label and that we are inside the loop
327+
if matches!(ent.kind(), AnyEntKind::LoopLabel) {
328+
if !find_outer_loop(parent, Some(label.item.name())) {
329+
diagnostics.error(
330+
&label.item.pos,
331+
format!("Cannot be used outside of loop '{}'", ent.designator()),
332+
);
333+
}
334+
} else {
298335
diagnostics.error(
299336
&label.item.pos,
300337
format!("Expected loop label, got {}", ent.describe()),
@@ -322,19 +359,8 @@ impl<'a> AnalyzeContext<'a> {
322359
diagnostics: &mut dyn DiagnosticHandler,
323360
) -> FatalResult {
324361
for statement in statements.iter_mut() {
325-
let parent = if statement.statement.can_have_label() {
326-
if let Some(id) = statement.label.as_ref().and_then(|label| label.decl) {
327-
self.arena.get(id)
328-
} else {
329-
// Generate an anonymous label if it is not explicitly defined
330-
self.arena.alloc(
331-
Designator::Anonymous(scope.next_anonymous()),
332-
Some(parent),
333-
Related::None,
334-
AnyEntKind::Label,
335-
None,
336-
)
337-
}
362+
let parent = if let Some(id) = statement.label.decl {
363+
self.arena.get(id)
338364
} else {
339365
parent
340366
};
@@ -352,6 +378,30 @@ enum SequentialRoot<'a> {
352378
Function(TypeEnt<'a>),
353379
}
354380

381+
fn find_outer_loop(ent: EntRef, label: Option<&Symbol>) -> bool {
382+
match ent.kind() {
383+
AnyEntKind::LoopLabel => {
384+
if let Some(label) = label {
385+
if matches!(ent.designator(), Designator::Identifier(ident) if ident == label) {
386+
return true;
387+
}
388+
} else {
389+
return true;
390+
}
391+
}
392+
AnyEntKind::Label => {}
393+
_ => {
394+
return false;
395+
}
396+
}
397+
398+
if let Some(parent) = ent.parent {
399+
find_outer_loop(parent, label)
400+
} else {
401+
false
402+
}
403+
}
404+
355405
impl<'a> From<EntRef<'a>> for SequentialRoot<'a> {
356406
fn from(value: EntRef<'a>) -> Self {
357407
match value.kind() {
@@ -362,7 +412,7 @@ impl<'a> From<EntRef<'a>> for SequentialRoot<'a> {
362412
SequentialRoot::Procedure
363413
}
364414
}
365-
AnyEntKind::Label => {
415+
AnyEntKind::Label | AnyEntKind::LoopLabel => {
366416
if let Some(parent) = value.parent {
367417
SequentialRoot::from(parent)
368418
} else {

vhdl_lang/src/analysis/tests/hierarchy.rs

+89
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,92 @@ end architecture;
206206
assert_eq!(root.find_implementation(ent), vec![comp]);
207207
assert_eq!(root.find_implementation(comp), vec![ent]);
208208
}
209+
210+
#[test]
211+
fn exit_and_next_outside_of_loop() {
212+
let mut builder = LibraryBuilder::new();
213+
let code = builder.code(
214+
"libname",
215+
"
216+
entity ent is
217+
end entity;
218+
219+
architecture a of ent is
220+
begin
221+
process
222+
begin
223+
exit;
224+
next;
225+
226+
loop
227+
exit;
228+
end loop;
229+
230+
loop
231+
next;
232+
end loop;
233+
end process;
234+
end architecture;
235+
",
236+
);
237+
238+
let (_, diagnostics) = builder.get_analyzed_root();
239+
check_diagnostics(
240+
diagnostics,
241+
vec![
242+
Diagnostic::error(code.s1("exit;"), "Exit can only be used inside a loop"),
243+
Diagnostic::error(code.s1("next;"), "Next can only be used inside a loop"),
244+
],
245+
);
246+
}
247+
248+
#[test]
249+
fn exit_and_next_label_outside_of_loop() {
250+
let mut builder = LibraryBuilder::new();
251+
let code = builder.code(
252+
"libname",
253+
"
254+
entity ent is
255+
end entity;
256+
257+
architecture a of ent is
258+
begin
259+
main: process
260+
begin
261+
good0: loop
262+
good1: loop
263+
exit good0;
264+
end loop;
265+
end loop;
266+
267+
bad0: loop
268+
exit;
269+
end loop;
270+
271+
l1: loop
272+
exit bad0;
273+
end loop;
274+
275+
l0: loop
276+
next bad0;
277+
end loop;
278+
end process;
279+
end architecture;
280+
",
281+
);
282+
283+
let (_, diagnostics) = builder.get_analyzed_root();
284+
check_diagnostics(
285+
diagnostics,
286+
vec![
287+
Diagnostic::error(
288+
code.sa("exit ", "bad0"),
289+
"Cannot be used outside of loop 'bad0'",
290+
),
291+
Diagnostic::error(
292+
code.sa("next ", "bad0"),
293+
"Cannot be used outside of loop 'bad0'",
294+
),
295+
],
296+
);
297+
}

vhdl_lang/src/ast.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ pub enum SequentialStatement {
949949
/// LRM 10. Sequential statements
950950
#[derive(PartialEq, Debug, Clone)]
951951
pub struct LabeledSequentialStatement {
952-
pub label: Option<WithDecl<Ident>>,
952+
pub label: WithDecl<Option<Ident>>,
953953
pub statement: SequentialStatement,
954954
}
955955

@@ -1075,7 +1075,7 @@ pub enum ConcurrentStatement {
10751075
/// LRM 11. Concurrent statements
10761076
#[derive(PartialEq, Debug, Clone)]
10771077
pub struct LabeledConcurrentStatement {
1078-
pub label: Option<WithDecl<Ident>>,
1078+
pub label: WithDecl<Option<Ident>>,
10791079
pub statement: ConcurrentStatement,
10801080
}
10811081

0 commit comments

Comments
 (0)