Skip to content

Commit a638fac

Browse files
committed
V622-017: Fix env grouping with non-default metadata.
The routine for grouping lexical envs would previously always flatten grouped envs. However, this process would discard the `default_md` field of the nested grouped envs. The implementation now decides to flatten grouped envs only if the resulting env behavior is the same.
1 parent 540f2d3 commit a638fac

File tree

7 files changed

+166
-12
lines changed

7 files changed

+166
-12
lines changed

langkit/support/langkit_support-lexical_envs_impl.adb

+12-12
Original file line numberDiff line numberDiff line change
@@ -1635,18 +1635,18 @@ package body Langkit_Support.Lexical_Envs_Impl is
16351635

16361636
procedure Append_Envs (E : Lexical_Env) is
16371637
begin
1638-
case E.Kind is
1639-
-- Flatten grouped envs
1640-
when Grouped =>
1641-
for C of Unwrap (E).Grouped_Envs.all loop
1642-
Append_Envs (C);
1643-
end loop;
1644-
when others =>
1645-
if not Already_Has (E) then
1646-
Inc_Ref (E);
1647-
V.Append (E);
1648-
end if;
1649-
end case;
1638+
-- Flatten grouped envs only when we don't lose the `default_md`
1639+
-- field of a nested grouped env.
1640+
if E.Kind in Grouped
1641+
and then Unwrap (E).Default_Md in Empty_Metadata | With_Md
1642+
then
1643+
for C of Unwrap (E).Grouped_Envs.all loop
1644+
Append_Envs (C);
1645+
end loop;
1646+
elsif not Already_Has (E) then
1647+
Inc_Ref (E);
1648+
V.Append (E);
1649+
end if;
16501650
end Append_Envs;
16511651
begin
16521652
if Envs'Length = 0 then
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import lexer_example
2+
3+
@with_lexer(foo_lexer)
4+
grammar foo_grammar {
5+
@main_rule main_rule <- list+(decl)
6+
decl <- Decl(Name(@identifier) "(" list*(ref) ")")
7+
ref <- Ref(Name(@identifier))
8+
}
9+
10+
@abstract class FooNode implements Node[FooNode] {
11+
12+
@memoized fun env_with_md(foo_node: FooNode, bar_node: FooNode): LexicalEnv[FooNode] =
13+
{
14+
val md1 = Metadata(foo_node=foo_node, bar_node=null);
15+
val md2 = Metadata(foo_node=null, bar_node=bar_node);
16+
17+
[[node.node_env()].env_group()].env_group()
18+
}
19+
20+
@export fun get_with_md(name: Symbol, foo_node: FooNode, bar_node: FooNode): FooNode =
21+
self.env_with_md(foo_node, bar_node).get_first(name)
22+
23+
@export fun get_foo_metadata(): FooNode = self.info.md.foo_node
24+
25+
@export fun get_bar_metadata(): FooNode = self.info.md.bar_node
26+
}
27+
28+
class Decl : FooNode {
29+
@parse_field name: Name
30+
@parse_field refs: ASTList[FooNode, Ref]
31+
}
32+
33+
class Name : FooNode implements TokenNode {
34+
}
35+
36+
class Ref : FooNode {
37+
@parse_field name: Name
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import sys
2+
3+
import libfoolang
4+
5+
6+
print('main.py: Running...')
7+
8+
ctx = libfoolang.AnalysisContext()
9+
u = ctx.get_from_file('main.txt')
10+
if u.diagnostics:
11+
for d in u.diagnostics:
12+
print(d)
13+
sys.exit(1)
14+
15+
decl_list = u.root
16+
foo = decl_list[0]
17+
bar = decl_list[1]
18+
19+
baz = decl_list.p_get_with_md("baz", foo, bar)
20+
print(baz.f_name.text + " has the following metadata:")
21+
print(" - foo_node: " + baz.p_get_foo_metadata.f_name.text)
22+
print(" - bar_node: " + baz.p_get_bar_metadata.f_name.text)
23+
24+
print('main.py: Done.')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
foo(
2+
bar
3+
baz
4+
)
5+
6+
bar()
7+
baz()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
main.py: Running...
2+
baz has the following metadata:
3+
- foo_node: foo
4+
- bar_node: bar
5+
main.py: Done.
6+
Done
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Test that nested grouped envs with non-null default metadata behave as
3+
expected.
4+
"""
5+
6+
from langkit.dsl import ASTNode, Field, Struct, T, UserField, env_metadata
7+
from langkit.envs import EnvSpec, add_env, add_to_env_kv
8+
from langkit.expressions import Entity, No, Self, Var, langkit_property
9+
10+
from utils import build_and_run
11+
12+
13+
@env_metadata
14+
class Metadata(Struct):
15+
foo_node = UserField(T.FooNode)
16+
bar_node = UserField(T.FooNode)
17+
18+
19+
class FooNode(ASTNode):
20+
@langkit_property(memoized=True)
21+
def env_with_md(foo_node=T.FooNode, bar_node=T.FooNode):
22+
md1 = Var(Metadata.new(
23+
foo_node=foo_node,
24+
bar_node=No(T.FooNode)
25+
))
26+
md2 = Var(Metadata.new(
27+
foo_node=No(T.FooNode),
28+
bar_node=bar_node
29+
))
30+
return Self.node_env.singleton.env_group(
31+
with_md=md1
32+
).singleton.env_group(
33+
with_md=md2
34+
)
35+
36+
@langkit_property(return_type=T.FooNode.entity, public=True)
37+
def get_with_md(
38+
name=T.Symbol,
39+
foo_node=T.FooNode,
40+
bar_node=T.FooNode
41+
):
42+
return Entity.env_with_md(foo_node, bar_node).get_first(name)
43+
44+
@langkit_property(return_type=T.FooNode, public=True)
45+
def get_foo_metadata():
46+
return Entity.info.md.foo_node
47+
48+
@langkit_property(return_type=T.FooNode, public=True)
49+
def get_bar_metadata():
50+
return Entity.info.md.bar_node
51+
52+
53+
class Name(FooNode):
54+
token_node = True
55+
56+
57+
class Decl(FooNode):
58+
name = Field()
59+
refs = Field()
60+
61+
env_spec = EnvSpec(
62+
add_to_env_kv(
63+
key=Self.name.symbol, value=Self
64+
),
65+
add_env()
66+
)
67+
68+
69+
class Ref(FooNode):
70+
name = Field()
71+
72+
env_spec = EnvSpec(add_to_env_kv(
73+
key=Self.name.symbol, value=Self
74+
))
75+
76+
77+
build_and_run(lkt_file='expected_concrete_syntax.lkt', py_script='main.py')
78+
print('Done')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
driver: python

0 commit comments

Comments
 (0)