Skip to content

Commit

Permalink
Merge branch 'topic/1137-2' into 'master'
Browse files Browse the repository at this point in the history
Refactor `ForLoopSpec` equations.

Closes #1137

See merge request eng/libadalang/libadalang!1468
  • Loading branch information
Roldak committed Nov 21, 2023
2 parents d0903b2 + 4a1d2ff commit 112d27f
Show file tree
Hide file tree
Showing 11 changed files with 608 additions and 313 deletions.
169 changes: 79 additions & 90 deletions ada/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -6625,13 +6625,28 @@ def is_iterator_type():
['Ada', 'Iterator_Interfaces'], UnitSpecification
))
typ = Var(Entity.cast(T.ClasswideTypeDecl).then(
lambda cw: cw.type_decl, default_val=Entity)
)
lambda cw: cw.type_decl, default_val=Entity
))
return Or(
typ.semantic_parent.semantic_parent.node == iifcs,
Entity.canonical_part.has_aspect("Iterable")
)

@langkit_property(return_type=T.BaseTypeDecl.entity, dynamic_vars=[origin])
def cursor_type():
"""
Assuming this is an iterator type, return the associated cursor
type.
"""
return (
# For containers and user defined iterator types, cursor type
# is defined by the `Cursor` type declaration.
Entity.children_env.get_first('Cursor')
.cast_or_raise(T.BaseTypeDecl)
# Check out cursor type for types with `Iterable` aspect
._or(Entity.cast(TypeDecl).iterable_cursor_type)
)

@langkit_property(dynamic_vars=[default_origin()], public=True)
def is_discrete_type():
"""
Expand Down Expand Up @@ -6980,6 +6995,18 @@ def is_limited_type():
def iterable_comp_type():
return No(T.BaseTypeDecl.entity)

@langkit_property(dynamic_vars=[origin])
def iterable_comp_type_or_null():
return If(
Self.is_null,
No(BaseTypeDecl.entity),
If(
Entity.is_implicit_deref,
Entity.accessed_type,
Entity
).iterable_comp_type
)

@langkit_property(return_type=Bool, dynamic_vars=[origin])
def matching_prefix_type(container_type=T.BaseTypeDecl.entity):
"""
Expand Down Expand Up @@ -7734,7 +7761,8 @@ def iterable_cursor_type():
return Entity.get_aspect_spec_expr('Iterable').then(
lambda it: it.cast(T.Aggregate).assocs.unpacked_params.find(
lambda sa: sa.name.name_is('First')
).assoc.expr.cast_or_raise(T.Name).referenced_decl.expr_type)
).assoc.expr.cast_or_raise(T.Name).referenced_decl.expr_type
)

@langkit_property()
def discrete_range():
Expand Down Expand Up @@ -12636,16 +12664,6 @@ def expected_type_var():
"""
)

xref_stop_resolution = Property(
# Pause resolution of ForLoopSpecs' iterator filter expression, so
# that the indexing variable's type can be fully inferred first.
# Otherwise, any reference to the indexing variable appearing in the
# filter expression will cause an infinite xref_equation recursion.
Self.parent.cast(T.ForLoopSpec).then(
lambda spec: spec.iter_filter == Self
)
)

@langkit_property(return_type=T.Bool)
def has_context_free_type():
"""
Expand Down Expand Up @@ -19402,7 +19420,7 @@ class ForLoopSpec(LoopSpec):
loop_type = Field(type=IterType)
has_reverse = Field(type=Reverse)
iter_expr = Field(type=T.AdaNode)
iter_filter = Field(type=T.Expr)
iter_filter = Field(type=T.ForLoopIterFilter)

@langkit_property(return_type=Bool)
def is_iterated_assoc_spec():
Expand All @@ -19412,37 +19430,6 @@ def is_iterated_assoc_spec():
"""
return Self.parent.is_a(IteratedAssoc)

@langkit_property(memoized=True, call_memoizable=True,
dynamic_vars=[env, origin])
def iter_type():
type_var = Var(Entity.iter_expr.cast_or_raise(Expr).type_var)

p = Var(Entity.iter_expr.resolve_names_internal_with_eq(
# Avoid resolving to a procedure
Predicate(AdaNode.is_not_null, type_var)

# If there is a type annotation, use it as the expected type for
# `iter_expr`.
& If(
Self.var_decl.id_type.is_null,
LogicTrue(),
Bind(Self.iter_expr.cast(Expr).expected_type_var,
Entity.var_decl.id_type.designated_type)
)
))

typ = Var(If(
p,
type_var.get_value.cast(T.BaseTypeDecl),
No(BaseTypeDecl.entity)
))

return origin.bind(Self.origin_node, If(
typ.is_implicit_deref,
typ.accessed_type,
typ
))

@langkit_property(return_type=Equation)
def xref_equation():
return Entity.var_decl.sub_equation & Self.loop_type.match(
Expand Down Expand Up @@ -19475,67 +19462,69 @@ def xref_equation():
# attribute on a subtype indication, in which case the logic is
# the same as above, either it's an expression that yields an
# iterator.
lambda t=T.Name: t.name_designated_type.then(
lambda t=T.Name: t.sub_equation & t.name_designated_type.then(
lambda typ:
t.sub_equation
& Bind(Self.var_decl.id.type_var, typ.canonical_type),
default_val=Entity.iterator_xref_equation
Bind(Self.var_decl.id.type_var, typ.canonical_type),
default_val=And(
Predicate(BaseTypeDecl.is_iterator_type, t.type_var),
Bind(t.type_var, Self.var_decl.id.type_var,
conv_prop=BaseTypeDecl.cursor_type)
)
),

lambda _: LogicTrue() # should never happen
),

# This is a for .. of
lambda _=IterType.alt_of: Let(lambda it_typ=Entity.iter_type: If(
it_typ.is_iterable_type,

# Then we want the type of the induction variable to be the
# component type of the type of the expression.
Bind(Self.var_decl.id.type_var, it_typ.iterable_comp_type),

LogicFalse()
))
) & If(
Entity.iter_filter.is_null,
LogicTrue(),
Bind(Self.iter_filter.expected_type_var, Self.bool_type)
& Bind(Self.iter_filter.type_var, Self.bool_type)
& Entity.iter_filter.sub_equation
)

@langkit_property(return_type=Equation, dynamic_vars=[env, origin])
def iterator_xref_equation():
iter_expr = Var(Entity.iter_expr.cast_or_raise(T.Expr))

p = Var(iter_expr.resolve_names_internal_with_eq(
Predicate(BaseTypeDecl.is_iterator_type,
iter_expr.type_var)
))

cursor_type = Var(
Let(
lambda tv=iter_expr.type_val:
# For containers and user defined iterator types, cursor type
# is defined by the `Cursor` type declaration.
tv.children_env.get_first('Cursor')
.cast_or_raise(T.BaseTypeDecl)
# Check out cursor type for types with `Iterable` aspect
._or(tv.cast(TypeDecl).iterable_cursor_type)
lambda _=IterType.alt_of: Entity.iter_expr.cast(Expr).then(
lambda iter_expr:
Entity.var_decl.id_type.then(
lambda typ:
Bind(iter_expr.expected_type_var, typ.designated_type),
default_val=LogicTrue()
)
& iter_expr.sub_equation
& Predicate(AdaNode.is_not_null, iter_expr.type_var)
& Bind(iter_expr.type_var, Self.var_decl.id.type_var,
conv_prop=BaseTypeDecl.iterable_comp_type_or_null),
default_val=LogicFalse()
)
)

return If(
p,
Bind(Self.var_decl.id.type_var, cursor_type),
LogicFalse()
)

# This spec is not a complete resolution context when part of an iterated
# component association: we must know the type of the enclosing aggregate
# to determine the type of the iteration variable in case of a `for I in`.
xref_entry_point = Property(Not(Self.is_iterated_assoc_spec))


class ForLoopIterFilter(AdaNode):
"""
Represent the ``when ...`` filter after a for loop specification. This
class has no RM existence, it is used internally to wrap the filtering
expression, so as to have a dedicated name resolution entry point for it
and make sure it is resolved separatly from the ``ForLoopSpec`` itself
(which it cannot influence anyway).
"""
expr = Field(type=T.Expr)

@langkit_property(return_type=Equation)
def xref_equation():
ignore(Var(Entity.parent.cast(ForLoopSpec).then(
lambda spec: If(
spec.is_iterated_assoc_spec,
spec.resolve_names_from_closest_entry_point,
True
)
)))
return And(
Bind(Self.expr.expected_type_var, Self.bool_type),
Entity.expr.sub_equation,
Entity.expr.matches_expected_formal_prim_type
)

xref_entry_point = Property(True)


class QuantifiedExpr(Expr):
"""
Quantified expression (:rmlink:`4.5.8`).
Expand Down
2 changes: 1 addition & 1 deletion ada/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,7 @@ def end_named_block():
IterType.alt_in("in") | IterType.alt_of("of"),
Reverse("reverse"),
A.discrete_range | A.discrete_subtype_indication | A.name,
Opt("when", A.expr)
Opt(ForLoopIterFilter("when", A.expr))
),

quantified_expr=QuantifiedExpr(
Expand Down
4 changes: 1 addition & 3 deletions testsuite/tests/name_resolution/container_aggregate/pkg.adb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ package body Pkg is

procedure P (Arr : Arr_T) is
Set1 : Set := [for E of Arr when E < 0 => E];
pragma Test_Statement;
Set2 : Set := [for E of Arr when E < 0 or else E > 0 => E];
pragma Test_Statement;
Set3 : Set := [for Item in 1 .. 5 => Item * 2];
pragma Test_Statement;
begin
null;
end P;
pragma Test_Block;
end Pkg;
Loading

0 comments on commit 112d27f

Please sign in to comment.