diff --git a/lkt/nodes.lkt b/lkt/nodes.lkt index 1881f7892..f14306e62 100644 --- a/lkt/nodes.lkt +++ b/lkt/nodes.lkt @@ -1076,77 +1076,72 @@ class Decl: LktNode implements DeclarationInterface { env_spec { add_all_to_env( - # If the type explicitly implements the ``Node`` trait, or it - # is annotated with "@root_node", consider it the root node of - # the language specification. - if - ( - self.full_decl()?.has_annotation(s"root_node") - or self.implements_node() - ) - and self.name() != s"RootNode__" - then - [ - EnvAssoc( - key=s"RootNode__", - value=node, - dest_env=DesignatedEnv( - kind=DesignatedEnvKind.direct_env, - env_name=null[Symbol], - direct_env=node.root_env() - ), - metadata=null[Metadata] + { + # Regular env entry, so that this declaration is available + # using its name in this module. + val regular_entry = EnvAssoc( + key=self.name(), + value=node, + dest_env=DesignatedEnv( + kind=DesignatedEnvKind.current_env, + env_name=null[Symbol], + direct_env=null[LexicalEnv] ), - EnvAssoc( - key=self.name(), - value=node, - dest_env=DesignatedEnv( - kind=DesignatedEnvKind.current_env, - env_name=null[Symbol], - direct_env=null[LexicalEnv] - ), - metadata=null[Metadata] - ) - ] - elif - self.full_decl()?.has_annotation(s"metadata") - and self.name() != s"Metadata" - then - [ - EnvAssoc( - key=s"Metadata", - value=node, - dest_env=DesignatedEnv( - kind=DesignatedEnvKind.direct_env, - env_name=null[Symbol], - direct_env=node.root_env() - ), - metadata=null[Metadata] - ), - EnvAssoc( - key=self.name(), - value=node, - dest_env=DesignatedEnv( - kind=DesignatedEnvKind.current_env, - env_name=null[Symbol], - direct_env=null[LexicalEnv] - ), - metadata=null[Metadata] - ) - ] - else - [ - EnvAssoc( - key=self.name(), - value=node, - dest_env=DesignatedEnv( - kind=DesignatedEnvKind.current_env, - env_name=null[Symbol], - direct_env=null[LexicalEnv] - ), - metadata=null[Metadata] - ) - ] + metadata=null[Metadata] + ); + + # If the type explicitly implements the ``Node`` trait, or it + # is annotated with "@root_node", consider it the root node of + # the language specification. + val root_node_entry = + if + ( + self.full_decl()?.has_annotation(s"root_node") + or self.implements_node() + ) + and self.name() != s"RootNode__" + then + [ + EnvAssoc( + key=s"RootNode__", + value=node, + dest_env=DesignatedEnv( + kind=DesignatedEnvKind.direct_env, + env_name=null[Symbol], + direct_env=node.root_env() + ), + metadata=null[Metadata] + ) + ] + else + []: EnvAssoc; + + # If the type is a metadata struct declaration, register it in + # the root scope under the appropriate symbol: see the doc for + # __default_metadata in the prelude unit. + val metadata_entry = + if self.full_decl()?.has_annotation(s"metadata") then + [ + EnvAssoc( + key=( + if node.is_from_prelude() + then s"__default_metadata" + else s"__user_metadata" + ), + value=node, + dest_env=DesignatedEnv( + kind=DesignatedEnvKind.direct_env, + env_name=null[Symbol], + direct_env=node.root_env() + ), + metadata=null[Metadata] + ) + ] + else + []: EnvAssoc; + + [regular_entry] & root_node_entry & metadata_entry + } ) } } @@ -5377,19 +5372,16 @@ class LangkitRoot: Decl { fun defined_scope(): LexicalEnv = node.children_env - |" Get the hidden environment in the prelude containing a default - |" declaration of the Metadata type, for when it is not defined by the - |" specification. - fun internal_env(): LexicalEnv = { - bind origin = null[Entity[LktNode]]; - - node.children_env.get_first(s"__internal").as[Decl].defined_scope() - } - |" Return the name of this Lkt module, or the empty symbol for the prelude. @external() fun module_name(): Symbol + |" Return the Metadata type: see the doc for __default_metadata in the + |" prelude unit. + fun resolve_metadata(): Entity[LktNode] = + node.root_env().get_first(s"__user_metadata") + or? node.root_env().get_first(s"__default_metadata") + |" An empty synthetic TypeRef list node. Used to generate synthetic type |" declarations. |" @@ -5417,14 +5409,21 @@ class LangkitRoot: Decl { cond=not node.is_from_prelude() ) - handle_children() - - # Make default declarations (Metadata) available in the prelude - reference( - [node.as[LktNode]], - LangkitRoot.internal_env, - cond=node.is_from_prelude() + # Add a dynamic entry to resolve to the right Metadata type + add_to_env_kv( + key=s"Metadata", + value=( + if node.is_from_prelude() then node else null[LktNode] + ), + dest_env=DesignatedEnv( + kind=DesignatedEnvKind.direct_env, + env_name=null[Symbol], + direct_env=node.root_env() + ), + resolver=LangkitRoot.resolve_metadata ) + + handle_children() } } diff --git a/lkt/prelude.lkt b/lkt/prelude.lkt index e9308c360..f33f92df9 100644 --- a/lkt/prelude.lkt +++ b/lkt/prelude.lkt @@ -831,15 +831,21 @@ struct Token { struct SourceLocation { } -struct __internal { - |" Placeholder type when the Langkit specification does not have a type - |" annotated with ``@metadata``. +struct __default_metadata { + |" Default Metadata type, to be used when the Langkit specification does + |" not have a type annotated with ``@metadata``. |" - |" This is hidden in its own scope and later has a reference in the - |" global lexical environemnt so that if no type is designated as metadata, - |" this types serves as fallback. + |" 1. This is hidden in a scope so that by default it is unreachable from + |" user code. It is still registered in the root scope as + |" ``__default_metadata`` (for convenient access to it). + |" + |" 2. If user code defines a Metadata struct, it is registered in the root + |" scope as ``__user_metadata``. + |" + |" 3. A dynamic entity resolver inserted in the root scope will resolve to + |" the user Metadata (if available) or the default one (if not). @metadata - struct __EmptyMetadata { + struct Metadata { } } diff --git a/testsuite/tests/lkt/lsp/complete/test.out b/testsuite/tests/lkt/lsp/complete/test.out index 8cdeb9fcb..0dd7b0f57 100644 --- a/testsuite/tests/lkt/lsp/complete/test.out +++ b/testsuite/tests/lkt/lsp/complete/test.out @@ -84,6 +84,8 @@ Completing ('Stru') - LogicVar: struct_kind - LookupKind: enum_kind - LspNodeInterface: interface_kind + - Metadata: struct_kind + - Metadata: struct_kind - NodeInterface: interface_kind - PreconditionFailure: struct_kind - PropertyError: struct_kind @@ -104,8 +106,7 @@ Completing ('Stru') - TokenNode: interface_kind - TypableNodeInterface: interface_kind - TypeInterface: interface_kind - - __EmptyMetadata: struct_kind - - __internal: struct_kind + - __default_metadata: struct_kind Completing ('v') - Address: struct_kind - AnalysisUnit: interface_kind @@ -138,6 +139,8 @@ Completing ('v') - LogicVar: struct_kind - LookupKind: enum_kind - LspNodeInterface: interface_kind + - Metadata: struct_kind + - Metadata: struct_kind - NodeInterface: interface_kind - PreconditionFailure: struct_kind - PropertyError: struct_kind @@ -160,8 +163,7 @@ Completing ('v') - TypeInterface: interface_kind - _: variable_kind - _: variable_kind - - __EmptyMetadata: struct_kind - - __internal: struct_kind + - __default_metadata: struct_kind - add_all_to_env: function_kind - add_env: function_kind - add_single_to_env: function_kind diff --git a/testsuite/tests/lkt/semantic/env_spec/test.out b/testsuite/tests/lkt/semantic/env_spec/test.out index 720ad2760..7c0bf1ca5 100644 --- a/testsuite/tests/lkt/semantic/env_spec/test.out +++ b/testsuite/tests/lkt/semantic/env_spec/test.out @@ -234,7 +234,7 @@ Expr has_type Id - has_type Entity[FooNode]) -> EnvAction"> + has_type Entity[FooNode]) -> EnvAction"> references Expr @@ -248,7 +248,7 @@ Expr has_type Id - has_type Entity[FooNode]) -> EnvAction"> + has_type Entity[FooNode]) -> EnvAction"> references Id @@ -282,11 +282,11 @@ Expr references Expr - has_type + has_type Id has_type None - references + references Id has_type None diff --git a/testsuite/tests/lkt/semantic/metadata/test.lkt b/testsuite/tests/lkt/semantic/metadata/test.lkt new file mode 100644 index 000000000..fa10dacb8 --- /dev/null +++ b/testsuite/tests/lkt/semantic/metadata/test.lkt @@ -0,0 +1,7 @@ +@metadata +struct Metadata { +} + +class FooNode implements Node[FooNode] { + fun get_md(): Metadata = self.info.md +} diff --git a/testsuite/tests/lkt/semantic/metadata/test.out b/testsuite/tests/lkt/semantic/metadata/test.out new file mode 100644 index 000000000..cbc5b31fc --- /dev/null +++ b/testsuite/tests/lkt/semantic/metadata/test.out @@ -0,0 +1,38 @@ +Resolving test.lkt +================== +Expr + has_type None + +Id + has_type None + references + +Id + has_type None + references + +Decl + has_type Metadata" test.lkt> + + Id + has_type None + references + + Expr + has_type + + Expr + has_type + + Id + has_type + references + + Id + has_type + references + + Id + has_type + references + diff --git a/testsuite/tests/lkt/semantic/metadata/test.yaml b/testsuite/tests/lkt/semantic/metadata/test.yaml new file mode 100644 index 000000000..7695167b5 --- /dev/null +++ b/testsuite/tests/lkt/semantic/metadata/test.yaml @@ -0,0 +1 @@ +driver: lkt