Skip to content

Commit

Permalink
Merge branch 'topic/fast_get_primitives' into 'master'
Browse files Browse the repository at this point in the history
Optimize implementation of the get_primitives property.

Closes #1422

See merge request eng/libadalang/libadalang!1707
  • Loading branch information
Roldak committed Jul 30, 2024
2 parents 19e5256 + 6e09d80 commit dceeda8
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 71 deletions.
134 changes: 73 additions & 61 deletions ada/nodes.lkt
Original file line number Diff line number Diff line change
Expand Up @@ -5227,71 +5227,83 @@ class BaseTypeDecl: BasicDecl {
|" defined on this type. Predefined operators are included in the result
|" iff ``include_predefined_operators`` is True. It defaults to False.
@exported
fun get_primitives(only_inherited: Bool = false, include_predefined_operators: Bool = false): Array[Entity[BasicDecl]] = {
val prim_env = if only_inherited then self.parent_primitives_env() else self.primitives_env();
val all_prims = prim_env.get(null[Symbol]).map((t) => t.as[BasicDecl]);
val bds = if include_predefined_operators then all_prims else all_prims.filter((p) => not p is SyntheticSubpDecl);

# Make sure to return only one instance of each primitive: the most
# "overriding" one.
bds.filter(
(a, i) => {
val a_spec = a.subp_spec_or_null();
val a_prim = a.info.md.primitive.as_bare_entity.as[BaseTypeDecl];
fun get_primitives(
only_inherited: Bool = false,
include_predefined_operators: Bool = false
): Array[Entity[BasicDecl]] = {
val prim_env = if only_inherited then
self.parent_primitives_env()
else
self.primitives_env();

bds.all(
(b, j) => {
val b_prim = b.info.md.primitive.as[BaseTypeDecl];
# First gather the set of names that primitives of this type can have
val all_prim_names = prim_env.get(null[Symbol]).map(
(t) => t.as[BasicDecl].defining_name().name_symbol()
).unique();

(
# Note that we don't want to use `match_name=True` in the
# call to `match_signature` below because it also compares
# the name of the parameters which we don't want to take
# into account here. Therefore, we first compare the names
# of the subprogram separately.
not a.defining_name().node.matches(b.defining_name().node)
) or (
# If two primitives have the same signature...
{
bind origin = b.origin_node();
# Next, for each of these names, we only want to keep the
# "most overriding" ones.
all_prim_names.mapcat((name) => {
val all_prims = prim_env.get(name).map((t) => t.as[BasicDecl]);
val bds = if include_predefined_operators then
all_prims
else
all_prims.filter((p) => not p is SyntheticSubpDecl);

not a_spec.match_signature(
b.subp_spec_or_null(), match_name=false, use_entity_info=true
)
}
) or {
val b_prim_ent = b_prim.as_bare_entity;

# Test if the type of the first primitive (a) derives
# from the type of the second primitive (b)...
if a_prim.has_base_type(b_prim_ent.node) then (
# Case a derives from b...
# If b also derives from a, it means the types are
# equal: both primitives are in fact the same
# subprogram, but the first one is the declaration and
# the second one is the body. In that case we decide to
# keep the body.
# Else if b does not derive from a, it means the
# primitive on a overrides the primitive on b, so
# return True.
i >= j or not b_prim_ent.has_base_type(a_prim.node)
) else (
# Case a does *not* derive from b...
# If b also does not derive from a, the two base types
# are unrelated, it means that the primitives are
# merged in a single one (remember their signature
# match). We keep the one that is inherited first with
# respect to the list of parents.
# But if b derives from a, we return False as we don't
# want to keep this primitive: we will keep the most
# inherited one (defined on b) later instead.
i <= j and not b_prim_ent.has_base_type(a_prim.node)
)
}
bds.filter((a, i) => {
val a_spec = a.subp_spec_or_null();
val a_prim =
a.info.md.primitive.as_bare_entity.as[BaseTypeDecl];

# Only keep primitive a if it is the "most overriding" one.
# So, the logic below checks that there isn't a primitive b
# that overrides it.
not bds.any((b, j) => {
val b_prim = b.info.md.primitive.as[BaseTypeDecl];

(i != j) and {
# If two primitives have the same signature...
bind origin = b.origin_node();

a_spec.match_signature(
b.subp_spec_or_null(),
match_name=false,
use_entity_info=true
)
} and {
val b_prim_ent = b_prim.as_bare_entity;

# Test if the type of the first primitive (a) derives
# from the type of the second primitive (b)...
if a_prim.has_base_type(b_prim_ent.node) then (
# Case a derives from b...
# If b also derives from a, it means the types are
# equal: both primitives are in fact the same
# subprogram, but the first one is the declaration
# and the second one is the body. In that case we
# decide to keep the body.
# Else if b does not derive from a, it means the
# primitive on a overrides the primitive on b, so
# return False.
i < j and b_prim_ent.has_base_type(a_prim.node)
) else (
# Case a does *not* derive from b...
# If b also does not derive from a, the two base
# types are unrelated, it means that the primitives
# are merged in a single one (remember their
# signature match). We keep the one that is
# inherited first with respect to the list of
# parents.
# But if b derives from a, we return True as we
# don't want to keep this primitive: we will keep
# the most inherited one (defined on b) later
# instead.
i > j or b_prim_ent.has_base_type(a_prim.node)
)
}
)
}
)
})
})
})
}

|" Return whether this type is an array type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ Working on node <ConcreteTypeDecl ["Child"] test.adb:16:7-16:52>
================================================================

Eval 'node.p_get_primitives()'
Result: [<SubpDecl ["Get2"] test.adb:6:7-6:49>, <SubpDecl ["Get1"] test.adb:19:7-19:59>]
Result: [<SubpDecl ["Get1"] test.adb:19:7-19:59>, <SubpDecl ["Get2"] test.adb:6:7-6:49>]
4 changes: 2 additions & 2 deletions testsuite/tests/properties/inherited_primitives/test.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ Primitives inherited by <ConcreteTypeDecl ["U"] test.adb:10:7-10:42>:
<ExprFunction ["F"] test.adb:5:7-5:42>
<NullSubpDecl ["G"] test.adb:6:7-6:35>
Primitives inherited by <ConcreteTypeDecl ["U"] test.adb:14:7-14:42>:
<NullSubpDecl ["G"] test.adb:6:7-6:35>
<ExprFunction ["F"] test.adb:16:7-16:53>
<NullSubpDecl ["G"] test.adb:6:7-6:35>
Primitives inherited by <ConcreteTypeDecl ["U"] test.adb:32:7-32:42>:
<ExprFunction ["F"] test.adb:16:7-16:53>
<NullSubpDecl ["G"] test.adb:6:7-6:35>
Expand All @@ -14,6 +14,6 @@ Primitives inherited by <ConcreteTypeDecl ["U"] test.adb:36:7-36:50>:
<NullSubpDecl ["B"] test.adb:26:7-26:35>
Primitives inherited by <ConcreteTypeDecl ["U"] test.adb:52:7-52:60>:
<NullSubpDecl ["A"] test.adb:42:7-42:35>
<NullSubpDecl ["C"] test.adb:46:7-46:35>
<NullSubpDecl ["B"] test.adb:53:7-53:35>
<NullSubpDecl ["C"] test.adb:46:7-46:35>
Done
12 changes: 6 additions & 6 deletions testsuite/tests/properties/inherited_primitives_2/test.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ Working on node <ConcreteTypeDecl ["D"] pkg-der.adb:4:4-4:37>
=============================================================

Eval 'node.p_get_primitives()'
Result: [<SubpDecl ["Priv"] pkg.ads:11:4-11:41>,
<NullSubpDecl ["Foo"] pkg-der.adb:8:4-8:34>]
Result: [<NullSubpDecl ["Foo"] pkg-der.adb:8:4-8:34>,
<SubpDecl ["Priv"] pkg.ads:11:4-11:41>]

Working on node <ConcreteTypeDecl ["U"] pkg-der.ads:2:4-2:29>
=============================================================

Eval 'node.p_get_primitives()'
Result: [<SubpDecl ["Foo"] pkg-der.ads:6:4-6:26>,
<NullSubpDecl ["Bar"] pkg-der.ads:12:4-12:34>]
Result: [<NullSubpDecl ["Bar"] pkg-der.ads:12:4-12:34>,
<SubpDecl ["Foo"] pkg-der.ads:6:4-6:26>]

Working on node <ConcreteTypeDecl ["U"] pkg-der.ads:9:4-9:37>
=============================================================

Eval 'node.p_get_primitives()'
Result: [<SubpDecl ["Priv"] pkg.ads:11:4-11:41>,
<SubpDecl ["Foo"] pkg-der.ads:6:4-6:26>,
Result: [<SubpDecl ["Foo"] pkg-der.ads:6:4-6:26>,
<SubpDecl ["Priv"] pkg.ads:11:4-11:41>,
<NullSubpDecl ["Bar"] pkg-der.ads:12:4-12:34>]

Working on node <ConcreteTypeDecl ["T"] pkg.ads:2:4-2:38>
Expand Down
2 changes: 1 addition & 1 deletion testsuite/tests/properties/is_inherited_primitive/test.out
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
== test.adb ==
For <ConcreteTypeDecl ["Parent"] test.adb:3:7-3:41>, <SubpDecl ["Get1"] test.adb:5:7-5:49> is not inherited
For <ConcreteTypeDecl ["Parent"] test.adb:3:7-3:41>, <SubpDecl ["Get2"] test.adb:6:7-6:49> is not inherited
For <ConcreteTypeDecl ["Child"] test.adb:16:7-16:52>, <SubpDecl ["Get2"] test.adb:6:7-6:49> is inherited
For <ConcreteTypeDecl ["Child"] test.adb:16:7-16:52>, <SubpDecl ["Get1"] test.adb:18:7-18:59> is not inherited
For <ConcreteTypeDecl ["Child"] test.adb:16:7-16:52>, <SubpDecl ["Get2"] test.adb:6:7-6:49> is inherited

Done

0 comments on commit dceeda8

Please sign in to comment.