From a638facb03edb4baefdf8f1819db4ca56f191a5b Mon Sep 17 00:00:00 2001 From: Romain Beguet Date: Tue, 16 Aug 2022 11:06:04 +0200 Subject: [PATCH] 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. --- .../langkit_support-lexical_envs_impl.adb | 24 +++--- .../expected_concrete_syntax.lkt | 38 +++++++++ .../grouped_env_default_md/main.py | 24 ++++++ .../grouped_env_default_md/main.txt | 7 ++ .../grouped_env_default_md/test.out | 6 ++ .../grouped_env_default_md/test.py | 78 +++++++++++++++++++ .../grouped_env_default_md/test.yaml | 1 + 7 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 testsuite/tests/lexical_envs/grouped_env_default_md/expected_concrete_syntax.lkt create mode 100644 testsuite/tests/lexical_envs/grouped_env_default_md/main.py create mode 100644 testsuite/tests/lexical_envs/grouped_env_default_md/main.txt create mode 100644 testsuite/tests/lexical_envs/grouped_env_default_md/test.out create mode 100644 testsuite/tests/lexical_envs/grouped_env_default_md/test.py create mode 100644 testsuite/tests/lexical_envs/grouped_env_default_md/test.yaml diff --git a/langkit/support/langkit_support-lexical_envs_impl.adb b/langkit/support/langkit_support-lexical_envs_impl.adb index 50f7ee1e0..274d301d6 100644 --- a/langkit/support/langkit_support-lexical_envs_impl.adb +++ b/langkit/support/langkit_support-lexical_envs_impl.adb @@ -1635,18 +1635,18 @@ package body Langkit_Support.Lexical_Envs_Impl is procedure Append_Envs (E : Lexical_Env) is begin - case E.Kind is - -- Flatten grouped envs - when Grouped => - for C of Unwrap (E).Grouped_Envs.all loop - Append_Envs (C); - end loop; - when others => - if not Already_Has (E) then - Inc_Ref (E); - V.Append (E); - end if; - end case; + -- Flatten grouped envs only when we don't lose the `default_md` + -- field of a nested grouped env. + if E.Kind in Grouped + and then Unwrap (E).Default_Md in Empty_Metadata | With_Md + then + for C of Unwrap (E).Grouped_Envs.all loop + Append_Envs (C); + end loop; + elsif not Already_Has (E) then + Inc_Ref (E); + V.Append (E); + end if; end Append_Envs; begin if Envs'Length = 0 then diff --git a/testsuite/tests/lexical_envs/grouped_env_default_md/expected_concrete_syntax.lkt b/testsuite/tests/lexical_envs/grouped_env_default_md/expected_concrete_syntax.lkt new file mode 100644 index 000000000..a7cbecb60 --- /dev/null +++ b/testsuite/tests/lexical_envs/grouped_env_default_md/expected_concrete_syntax.lkt @@ -0,0 +1,38 @@ +import lexer_example + +@with_lexer(foo_lexer) +grammar foo_grammar { + @main_rule main_rule <- list+(decl) + decl <- Decl(Name(@identifier) "(" list*(ref) ")") + ref <- Ref(Name(@identifier)) +} + +@abstract class FooNode implements Node[FooNode] { + + @memoized fun env_with_md(foo_node: FooNode, bar_node: FooNode): LexicalEnv[FooNode] = + { + val md1 = Metadata(foo_node=foo_node, bar_node=null); + val md2 = Metadata(foo_node=null, bar_node=bar_node); + + [[node.node_env()].env_group()].env_group() + } + + @export fun get_with_md(name: Symbol, foo_node: FooNode, bar_node: FooNode): FooNode = + self.env_with_md(foo_node, bar_node).get_first(name) + + @export fun get_foo_metadata(): FooNode = self.info.md.foo_node + + @export fun get_bar_metadata(): FooNode = self.info.md.bar_node +} + +class Decl : FooNode { + @parse_field name: Name + @parse_field refs: ASTList[FooNode, Ref] +} + +class Name : FooNode implements TokenNode { +} + +class Ref : FooNode { + @parse_field name: Name +} diff --git a/testsuite/tests/lexical_envs/grouped_env_default_md/main.py b/testsuite/tests/lexical_envs/grouped_env_default_md/main.py new file mode 100644 index 000000000..4f757e25c --- /dev/null +++ b/testsuite/tests/lexical_envs/grouped_env_default_md/main.py @@ -0,0 +1,24 @@ +import sys + +import libfoolang + + +print('main.py: Running...') + +ctx = libfoolang.AnalysisContext() +u = ctx.get_from_file('main.txt') +if u.diagnostics: + for d in u.diagnostics: + print(d) + sys.exit(1) + +decl_list = u.root +foo = decl_list[0] +bar = decl_list[1] + +baz = decl_list.p_get_with_md("baz", foo, bar) +print(baz.f_name.text + " has the following metadata:") +print(" - foo_node: " + baz.p_get_foo_metadata.f_name.text) +print(" - bar_node: " + baz.p_get_bar_metadata.f_name.text) + +print('main.py: Done.') diff --git a/testsuite/tests/lexical_envs/grouped_env_default_md/main.txt b/testsuite/tests/lexical_envs/grouped_env_default_md/main.txt new file mode 100644 index 000000000..0a25629a1 --- /dev/null +++ b/testsuite/tests/lexical_envs/grouped_env_default_md/main.txt @@ -0,0 +1,7 @@ +foo( + bar + baz +) + +bar() +baz() diff --git a/testsuite/tests/lexical_envs/grouped_env_default_md/test.out b/testsuite/tests/lexical_envs/grouped_env_default_md/test.out new file mode 100644 index 000000000..2cea2f648 --- /dev/null +++ b/testsuite/tests/lexical_envs/grouped_env_default_md/test.out @@ -0,0 +1,6 @@ +main.py: Running... +baz has the following metadata: + - foo_node: foo + - bar_node: bar +main.py: Done. +Done diff --git a/testsuite/tests/lexical_envs/grouped_env_default_md/test.py b/testsuite/tests/lexical_envs/grouped_env_default_md/test.py new file mode 100644 index 000000000..0e24c9626 --- /dev/null +++ b/testsuite/tests/lexical_envs/grouped_env_default_md/test.py @@ -0,0 +1,78 @@ +""" +Test that nested grouped envs with non-null default metadata behave as +expected. +""" + +from langkit.dsl import ASTNode, Field, Struct, T, UserField, env_metadata +from langkit.envs import EnvSpec, add_env, add_to_env_kv +from langkit.expressions import Entity, No, Self, Var, langkit_property + +from utils import build_and_run + + +@env_metadata +class Metadata(Struct): + foo_node = UserField(T.FooNode) + bar_node = UserField(T.FooNode) + + +class FooNode(ASTNode): + @langkit_property(memoized=True) + def env_with_md(foo_node=T.FooNode, bar_node=T.FooNode): + md1 = Var(Metadata.new( + foo_node=foo_node, + bar_node=No(T.FooNode) + )) + md2 = Var(Metadata.new( + foo_node=No(T.FooNode), + bar_node=bar_node + )) + return Self.node_env.singleton.env_group( + with_md=md1 + ).singleton.env_group( + with_md=md2 + ) + + @langkit_property(return_type=T.FooNode.entity, public=True) + def get_with_md( + name=T.Symbol, + foo_node=T.FooNode, + bar_node=T.FooNode + ): + return Entity.env_with_md(foo_node, bar_node).get_first(name) + + @langkit_property(return_type=T.FooNode, public=True) + def get_foo_metadata(): + return Entity.info.md.foo_node + + @langkit_property(return_type=T.FooNode, public=True) + def get_bar_metadata(): + return Entity.info.md.bar_node + + +class Name(FooNode): + token_node = True + + +class Decl(FooNode): + name = Field() + refs = Field() + + env_spec = EnvSpec( + add_to_env_kv( + key=Self.name.symbol, value=Self + ), + add_env() + ) + + +class Ref(FooNode): + name = Field() + + env_spec = EnvSpec(add_to_env_kv( + key=Self.name.symbol, value=Self + )) + + +build_and_run(lkt_file='expected_concrete_syntax.lkt', py_script='main.py') +print('Done') diff --git a/testsuite/tests/lexical_envs/grouped_env_default_md/test.yaml b/testsuite/tests/lexical_envs/grouped_env_default_md/test.yaml new file mode 100644 index 000000000..30423a038 --- /dev/null +++ b/testsuite/tests/lexical_envs/grouped_env_default_md/test.yaml @@ -0,0 +1 @@ +driver: python