diff --git a/ada/ast.py b/ada/ast.py index a416d86f6..de7cfa4d9 100644 --- a/ada/ast.py +++ b/ada/ast.py @@ -1115,21 +1115,32 @@ def resolve_names(): origin.bind(Self.origin_node, Entity.resolve_names_internal(False)) ) - @langkit_property(return_type=T.LexicalEnv) - def resolve_names_from_closest_entry_point_impl(): + @langkit_property(return_type=Bool) + def resolve_names_from_closest_entry_point(): """ - Implementation helper for ``resolve_names_from_closest_entry_point``. - Instead of returning a Boolean, it returns a LexicalEnv, which is - either None (indicating that resolution failed), or contains the - lexical environment which the children of that node should bind when - resolving their own names. This allows propagating the initial env - we got from ``Entity.xref_initial_env`` on the closest xref entry - point. + Resolve names from the closest entry point up to this node. Note that + unlike ``resolve_names``, this will *not* trigger resolution of every + node with stop_resolution that lie in the sub-tree formed by the + closest entry point. It will only resolve those that are in the path to + resolving Self. Consider for example the following entry point: + + .. code:: + + R := (A, B); + + Since aggregate association nodes have ``stop_resolution`` set to True, + calling ``resolve_names_from_closest_entry_point`` on ``B`` will + resolve nodes ``R`` and ``B`` but not ``A``, because ``A`` does not lie + on the path to ``B``. + + This can be useful for resolving aggregates of variant records, because + resolution of a component association can safely call the resolution + of a discriminant association without triggering an infinite recursion, + as both are on different "paths". """ return If( - # This is the closest entry point: resolve its names and return - # its `xref_initial_env` if resolution succeeded, so that children - # will be able to use it to resolve their own names. + # This is the closest entry point: resolve its names and stop the + # recursion. Entity.xref_entry_point, env.bind( Entity.xref_initial_env, @@ -1137,78 +1148,35 @@ def resolve_names_from_closest_entry_point_impl(): Self.origin_node, entry_point.bind( Self, - If(Entity.resolve_own_names(False), - env, - No(LexicalEnv)) + Entity.resolve_own_names(False) ) ) ), - Let( - # Recurse in order to resolve names from the closest entry - # point: `res` will contain the environment to use if we need - # to resolve names inside Self, or None if resolution failed. - lambda - res=Entity.parent - ._.resolve_names_from_closest_entry_point_impl: - - env.bind( - res, + # Otherwise, recurse on the parent + Entity.parent._.resolve_names_from_closest_entry_point.then( + lambda _: env.bind( + Entity.xref_initial_env, origin.bind( Self.origin_node, - Cond( - # Resolution failed for the parent, so return None - # as well. - res == No(T.LexicalEnv), - res, - + If( # Resolution succeeded for the parent and this is a - # stop resolution, so re-use the parent environment - # to resolve Self's names. + # stop resolution, so resolve own names as well. Entity.xref_stop_resolution, entry_point.bind( Self, - If(Entity.resolve_own_names(False), - res, - No(LexicalEnv)) + Entity.resolve_own_names(False) ), - # Resolution succeeded but there is nothing to do - # on that particular node: return the parent - # environment, so that deeper children can use it. - res + # Resolution succeeded and there is nothing to do + # on that particular node: return successfully. + True ) ) ) ) ) - @langkit_property(return_type=Bool) - def resolve_names_from_closest_entry_point(): - """ - Resolve names from the closest entry point up to this node. Note that - unlike ``resolve_names``, this will *not* trigger resolution of every - node with stop_resolution that lie in the sub-tree formed by the - closest entry point. It will only resolve those that are in the path to - resolving Self. Consider for example the following entry point: - - .. code:: - - R := (A, B); - - Since aggregate association nodes have ``stop_resolution`` set to True, - calling ``resolve_names_from_closest_entry_point`` on ``B`` will - resolve nodes ``R`` and ``B`` but not ``A``, because ``A`` does not lie - on the path to ``B``. - - This can be useful for resolving aggregates of variant records, because - resolution of a component association can safely call the resolution - of a discriminant association without triggering an infinite recursion, - as both are on different "paths". - """ - result = Var(Entity.resolve_names_from_closest_entry_point_impl) - return result != No(LexicalEnv) - @langkit_property(return_type=T.SolverDiagnostic.array, external=True, call_memoizable=True, uses_entity_info=True, uses_envs=True) diff --git a/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.adb b/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.adb new file mode 100644 index 000000000..8f0d1179d --- /dev/null +++ b/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.adb @@ -0,0 +1,23 @@ +pragma Ada_2022; + +procedure Test is + type A is array (Natural range 0 .. 1) of Integer; + + function F return A is + (declare + I : constant Integer := 0; + J : constant Integer := 1; + begin (I, J)); + --% node.findall(lal.AggregateAssoc)[0].f_r_expr.p_referenced_decl() + + type B is array (Natural range 0 .. 1) of A; + K : Integer := 3; + + function H return B is + (declare + K : constant Integer := 2; + begin (B'First => (K, K), B'Last => (K, K))); + --% node.findall(lal.AggregateAssoc)[-1].f_r_expr.p_referenced_decl() +begin + null; +end; diff --git a/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.out b/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.out new file mode 100644 index 000000000..af6401571 --- /dev/null +++ b/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.out @@ -0,0 +1,11 @@ +Working on node +======================================================= + +Eval 'node.findall(lal.AggregateAssoc)[0].f_r_expr.p_referenced_decl()' +Result: + +Working on node +======================================================== + +Eval 'node.findall(lal.AggregateAssoc)[-1].f_r_expr.p_referenced_decl()' +Result: diff --git a/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.yaml b/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.yaml new file mode 100644 index 000000000..35ad4d5c4 --- /dev/null +++ b/testsuite/tests/name_resolution/declare_expr_aggregate_2/test.yaml @@ -0,0 +1,2 @@ +driver: inline-playground +input_sources: [test.adb]