forked from AdaCore/libadalang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathast.py
9366 lines (7555 loc) · 304 KB
/
ast.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from __future__ import absolute_import, division, print_function
from langkit.diagnostics import check_source_language
from langkit.dsl import (
AbstractField, AnalysisUnitKind, AnalysisUnit, Annotations, ASTNode, Bool,
EnumNode, Equation, Field, LexicalEnv, Int, LogicVar, LookupKind as LK,
NullField, Struct, Symbol, T, UserField, abstract, env_metadata,
has_abstract_list, synthetic
)
from langkit.envs import (
EnvSpec, RefKind, add_to_env, add_env, call_env_hook, handle_children, do,
reference, set_initial_env
)
from langkit.expressions import (
AbstractKind, AbstractProperty, And, ArrayLiteral as Array, BigIntLiteral,
Bind, Cond, DynamicVariable, EmptyEnv, Entity, If, Let, Literal, No, Not,
Or, Property, PropertyError, Self, Var, Try, ignore, langkit_property
)
from langkit.expressions.logic import LogicFalse, LogicTrue, Predicate
env = DynamicVariable('env', LexicalEnv)
origin = DynamicVariable('origin', T.AdaNode)
imprecise_fallback = DynamicVariable('imprecise_fallback', Bool)
UnitSpecification = AnalysisUnitKind.unit_specification
UnitBody = AnalysisUnitKind.unit_body
noprims = {'inherited_primitives': False, 'others': True}
def bind_origin(node, expr):
"""
Bind the origin iff we're in the definition of an aspect clause where
sequential lookup needs to be deactivated.
"""
return origin.bind(If(node.in_aspect, No(T.AdaNode), node), expr)
def default_origin():
"""
Helper to return an origin dynamic param spec wich defaults to
No(AdaNode).
"""
return (origin, No(T.AdaNode))
def default_imprecise_fallback():
"""
Helper to return an imprecise fallback dynamic param spec which defaults to
False.
"""
return (imprecise_fallback, False)
def entity_no_md(type, node, rebindings, from_rebound):
return Let(lambda n=node: type.entity.new(
node=n,
info=If(n.is_null, No(T.entity_info), T.entity_info.new(
rebindings=rebindings,
md=No(T.env_md),
from_rebound=from_rebound
))
))
def TypeBind(*args, **kwargs):
check_source_language(
'eq_prop' not in kwargs.keys(),
"You cannot pass an eq_prop to TypeBind"
)
kwargs['eq_prop'] = BaseTypeDecl.matching_type
return Bind(*args, **kwargs)
def bool_bind(type_var):
"""
Decouple the logic of binding to a Boolean type. We use
matching_formal_prim_type because in name resolution, Ada expects any type
derived from bool.
"""
return Bind(type_var, Self.bool_type,
eq_prop=BaseTypeDecl.matching_formal_prim_type)
def universal_int_bind(type_var):
"""
Return an equation that will bind type_var to any integer value,
corresponding to the notion of universal_integer in the Ada RM.
"""
return TypeBind(type_var, Self.universal_int_type)
def universal_real_bind(type_var):
return TypeBind(type_var, Self.universal_real_type)
def ref_used_packages():
"""
If Self is a library item or a subunit, reference the environments for
packages that are used at the top-level here. See
UsePackageClause's ref_env_nodes for the rationale.
"""
return reference(
Self.top_level_use_package_clauses,
through=T.Name.use_package_name_designated_env,
cond=Self.parent.is_a(T.LibraryItem, T.Subunit)
)
def ref_generic_formals():
"""
If Self is a generic package/subprogram and not a library item,
then the generic formals are not available in parent
environments. Make them available with ref_envs.
"""
return reference(
Self.cast(T.AdaNode).to_array,
through=T.AdaNode.nested_generic_formal_part,
cond=Not(Self.is_unit_root),
kind=RefKind.prioritary,
shed_corresponding_rebindings=True,
)
def add_to_env_kv(key, val, *args, **kwargs):
"""
Wrapper around envs.add_to_env, that takes a key and a val expression, and
creates the intermediate env_assoc Struct.
"""
return add_to_env(
T.env_assoc.new(key=key, val=val), *args, **kwargs
)
def env_mappings(defining_names, entity):
"""
Creates an env mapping array from a list of BaseId to be used as keys, and
an entity to be used as value in the mappings.
"""
return defining_names.map(
lambda n: T.env_assoc.new(key=n.name_symbol, val=entity)
)
def env_get(env, symbol, lookup=None, from_node=No(T.AdaNode),
categories=None):
"""
Wrapper for env.get. Refines from_node so that it starts from the closest
BasicSubpDecl / GenericInstantiation.
(see AdaNode.env_get_real_from_node).
"""
return env.get(
symbol, lookup, Self.env_get_real_from_node(from_node), categories
)
def env_get_first(env, symbol, lookup=None, from_node=No(T.AdaNode),
categories=None):
"""
Wrapper for env.get_first. Refines from_node so that it starts from the
closest BasicSubpDecl / GenericInstantiation.
(see AdaNode.env_get_real_from_node).
"""
return env.get_first(
symbol, lookup, Self.env_get_real_from_node(from_node), categories
)
def new_metadata(**kwargs):
"""
Constructor for Metadata. Waiting on default values for structs.
"""
source = None
if "source" in kwargs:
source = kwargs["source"]
del kwargs["source"]
vals = [
("dottable_subp", False),
("primitive", No(T.AdaNode)),
("primitive_real_type", No(T.AdaNode)),
("access_entity", False),
("is_call", False),
]
for k, v in vals:
if k not in kwargs:
kwargs[k] = v if not source else getattr(Entity.info.md, k)
return Metadata.new(**kwargs)
@env_metadata
class Metadata(Struct):
dottable_subp = UserField(
Bool, doc="Whether the stored element is a subprogram accessed through"
" the dot notation"
)
access_entity = UserField(
Bool,
doc="Whether the accessed entity is an anonymous access to it or not."
)
is_call = UserField(
Bool,
doc="Whether the entity represents a call in the original context"
)
primitive = UserField(
T.AdaNode,
doc="The type for which this subprogram is a primitive, if any"
)
primitive_real_type = UserField(
T.AdaNode,
doc="The type for which this subprogram is a primitive, if any"
)
@abstract
class AdaNode(ASTNode):
"""
Root node class for the Ada syntax tree.
"""
annotations = Annotations(
generic_list_type='AdaList',
warn_on_node=True
)
expression_type = Property(
No(T.BaseTypeDecl.entity),
type=T.BaseTypeDecl.entity,
public=True,
doc="""
Return the declaration corresponding to the type of this expression
after name resolution.
"""
)
in_aspect = Property(Not(Self.parents.find(
lambda p: p.cast(T.AspectAssoc).then(
lambda a: a.id.as_bare_entity.name_symbol.any_of('Pre', 'Post')
)
).is_null))
empty_env = Property(
Self.parents.find(lambda p: p.is_a(T.CompilationUnit))
.cast(T.CompilationUnit).get_empty_env,
)
@langkit_property(return_type=T.BasicDecl,
ignore_warn_on_node=True, uses_entity_info=False)
def get_root_decl():
"""
Unit method. Return the root decl for this node's unit.
"""
return Self.unit.root._.match(
lambda cu=T.CompilationUnit: cu.body.match(
lambda su=T.Subunit: su.body,
lambda li=T.LibraryItem: li.item,
lambda _: No(T.BasicDecl)
),
lambda _: No(T.BasicDecl),
)
@langkit_property(return_type=Bool)
def is_children_env(parent=LexicalEnv, current_env=LexicalEnv):
"""
Static property. Will return True if current_env is a children of
parent.
"""
return Cond(
current_env == parent, True,
current_env.is_null, False,
Self.is_children_env(parent, current_env.env_parent)
)
@langkit_property(return_type=T.AdaNode.entity)
def trigger_access_entity(val=T.Bool):
"""
Return Self as an entity, but with the ``access_entity`` field set to
val. Helper for the 'Unrestricted_Access machinery.
"""
new_md = Var(new_metadata(source=Entity.info.md, access_entity=val))
return AdaNode.entity.new(
node=Entity.node, info=T.entity_info.new(
rebindings=Entity.info.rebindings,
md=new_md,
from_rebound=Entity.info.from_rebound
)
)
@langkit_property(return_type=T.AdaNode.entity)
def trigger_is_call():
"""
Return Self as an entity, but with the ``is_call`` md field set to
True.
"""
return AdaNode.entity.new(
node=Entity.node,
info=T.entity_info.new(
rebindings=Entity.info.rebindings,
md=new_metadata(source=Entity.info.md, is_call=True),
from_rebound=Entity.info.from_rebound
)
)
@langkit_property(public=True,
dynamic_vars=[default_imprecise_fallback()])
def referenced_decl():
"""
Return the declaration this node references after name resolution.
If imprecise_fallback is True, errors raised during resolution of the
xref equation are catched and a fallback mechanism is triggered, which
tries to find the referenced declaration in an ad-hoc way.
"""
return Entity.referenced_decl_internal(False)
@langkit_property(public=True, return_type=T.DefiningName.entity,
dynamic_vars=[default_imprecise_fallback()])
def xref():
"""
Return a cross reference from this node to a defining identifier.
"""
return No(T.DefiningName.entity)
@langkit_property(public=True, return_type=T.BasicDecl.entity.array)
def complete():
"""
Return possible completions at this point in the file.
"""
return Self.children_env.get(No(Symbol)).map(
lambda n: n.cast(T.BasicDecl)
)
@langkit_property(public=True,
dynamic_vars=[default_imprecise_fallback()])
def referenced_decl_internal(try_immediate=Bool):
"""
Return the declaration this node references. Try not to run name res if
already resolved. INTERNAL USE ONLY.
"""
# TODO: remove from public API
ignore(try_immediate)
return No(T.BasicDecl.entity)
@langkit_property(public=False,
dynamic_vars=[default_imprecise_fallback()])
def referenced_decl_internal_helper(ref_var=T.LogicVar,
try_immediate=Bool):
"""
Helper to generate the piece of logic that is common to all
"referenced_decl_internal" implementations. "ref_var" is the logic
variable that contains the reference to return.
"""
return If(
imprecise_fallback,
Let(lambda v=Try(
Self.logic_val(Entity, ref_var, try_immediate),
LogicValResult.new(success=False, value=No(AdaNode.entity))
): Let(
lambda decl=v.value.cast(T.BasicDecl.entity): If(
v.success & (decl == v.value),
decl,
Entity.cast(T.Expr)._.first_corresponding_decl
)
)),
Self.logic_val(Entity, ref_var, try_immediate)
.value.cast_or_raise(T.BasicDecl.entity)
)
@langkit_property(public=True)
def generic_instantiations():
"""
Return the potentially empty list of generic package/subprogram
instantiations that led to the creation of this entity. Outer-most
instantiations appear last.
"""
return Self.generic_instantiations_internal(Entity.info.rebindings)
@langkit_property(return_type=T.GenericInstantiation.entity.array)
def generic_instantiations_internal(r=T.EnvRebindings):
return If(
r == No(T.EnvRebindings),
No(T.GenericInstantiation.entity.array),
Let(lambda
head=(r.rebindings_new_env.env_node
.cast_or_raise(T.GenericInstantiation).as_bare_entity),
tail=Self.generic_instantiations_internal(r.rebindings_parent):
head.singleton.concat(tail))
)
# We mark this property as memoizable because for the moment, we only ever
# get the first result of logic resolution, so we only ever want the result
# of the first evaluation of this property. When we change that, we'll
# probably change the solving API anyway.
@langkit_property(call_memoizable=True, return_type=T.LogicValResult)
def logic_val(from_node=T.AdaNode.entity, lvar=LogicVar,
try_immediate=(Bool, False)):
success = Var(If(
try_immediate & Not(lvar.get_value.is_null),
True,
from_node.parents.find(lambda p: p.xref_entry_point).resolve_names
))
return LogicValResult.new(success=success, value=If(
success, lvar.get_value, No(T.AdaNode.entity)
))
@langkit_property(return_type=T.AdaNode.entity)
def semantic_parent_helper(env=LexicalEnv):
return env.then(lambda env: env.env_node.as_entity._or(
Entity.semantic_parent_helper(env.env_parent)
))
@langkit_property(public=True)
def semantic_parent():
"""
Return the semantic parent for this node, if applicable, null
otherwise.
"""
return Entity.semantic_parent_helper(Entity.node_env)
@langkit_property(
return_type=AnalysisUnit, external=True, uses_entity_info=False,
uses_envs=False,
call_non_memoizable_because='Getting an analysis unit cannot appear'
' in a memoized context'
)
def get_unit(name=Symbol.array, kind=AnalysisUnitKind,
load_if_needed=Bool):
"""
Return the analysis unit for the given "kind" corresponding to this
Name. Return null if this is an illegal unit name, or if
"load_if_needed" is false and the unit is not loaded yet.
"""
pass
@langkit_property(return_type=T.AdaNode, uses_entity_info=False,
ignore_warn_on_node=True, call_memoizable=True)
def get_unit_root_decl(name=Symbol.array, kind=AnalysisUnitKind):
"""
If the corresponding analysis unit is loaded, return the root decl
node for the given analysis unit "kind" and correpsonding to the
name "name". If it's not loaded, return none.
"""
# Because we don't load the unit when it's not already there, it is
# safe to use this property in a memoized context.
u = Var(Self.get_unit(name, kind, False))
return u._.root._.get_root_decl
@langkit_property(public=True, return_type=AnalysisUnit.array,
external=True, uses_entity_info=False, uses_envs=False)
def filter_is_imported_by(units=AnalysisUnit.array, transitive=Bool):
"""
Filters out among the list of given units those that cannot refer to
the unit in which this node lies. If transitive is True, the whole
transitive closure of imports will be used to find a reference to the
unit of this node.
"""
pass
@langkit_property(kind=AbstractKind.abstract_runtime_check,
return_type=Equation, dynamic_vars=[env, origin])
def xref_equation():
"""
This is the base property for constructing equations that, when solved,
will resolve names and types for every sub expression of the expression
you call it on. Note that if you call that on any expression, in some
context it might lack full information and return multiple solutions.
If you want completely precise resolution, you must call that on the
outermost node that supports xref_equation.
"""
pass
xref_stop_resolution = Property(False)
stop_resolution_equation = Property(LogicTrue())
@langkit_property(return_type=Equation, dynamic_vars=[env, origin])
def sub_equation():
"""
Wrapper for xref_equation, meant to be used inside of xref_equation
when you want to get the sub equation of a sub expression. It is
used to change the behavior when xref_equation is called from
another xref_equation call, or from the top level, so that we can do
resolution in several steps.
"""
return If(Entity.xref_stop_resolution,
Entity.stop_resolution_equation,
Entity.xref_equation)
@langkit_property(return_type=Bool, dynamic_vars=[env, origin])
def resolve_names_internal(initial=Bool, additional_equation=Equation):
"""
Internal helper for resolve_names, implementing the recursive logic.
"""
solve_xref = Var(initial | Self.xref_stop_resolution)
i = Var(If(
solve_xref,
(Entity.xref_equation & additional_equation).solve,
True)
)
j = Var(Self.children.all(lambda c: c.then(
# Do not explore nodes that are xref entry points, and are not the
# initial node.
lambda c: If(
c.xref_entry_point,
True,
c.as_entity.resolve_names_internal(False, LogicTrue()),
),
default_val=True
)))
return i & j
xref_entry_point = Property(
False,
public=True,
doc="""
Designates entities that are entry point for the xref solving
infrastructure. If this returns true, then resolve_names can be called
on it.
"""
)
@langkit_property(return_type=Bool, public=True,
memoized=True, call_memoizable=True)
def resolve_names():
"""
This will resolve names for this node. If the operation is successful,
then type_var and ref_var will be bound on appropriate subnodes of the
statement.
"""
return env.bind(
Entity.children_env,
bind_origin(Self, Entity.resolve_names_internal(True, LogicTrue()))
)
# TODO: Navigation properties are not ready to deal with units containing
# multiple packages.
body_unit = Property(
Self.top_level_decl(Self.unit)._.match(
lambda body=T.Body: body.unit,
lambda decl=T.BasicDecl:
decl.as_bare_entity.defining_name.referenced_unit(UnitBody),
),
public=True, doc="""
If this unit has a body, fetch and return it.
"""
)
spec_unit = Property(
Self.top_level_decl(Self.unit)
.cast(T.Body)._.as_bare_entity.defining_name
.referenced_unit_or_null(UnitSpecification),
public=True, doc="""
If this unit has a spec, fetch and return it. Return the null analysis
unit otherwise. Note that this returns null for specs, as they don't
have another spec themselves.
"""
)
@langkit_property(return_type=LexicalEnv)
def parent_unit_env_helper(unit=AnalysisUnit, env=LexicalEnv):
return env.env_parent.then(lambda parent_env: parent_env.env_node.then(
lambda parent_node: If(
parent_node.unit == unit,
Self.parent_unit_env_helper(unit, parent_env),
parent_env
)
))
@langkit_property()
def parent_unit_env(env=LexicalEnv):
"""
Given env's AnalysisUnit, return the first env that has a different
analysis unit in the env parent chain.
"""
return env.then(
lambda env: Self.parent_unit_env_helper(env.env_node.unit, env)
)
@langkit_property(return_type=T.AnalysisUnit, public=True,
external=True, uses_entity_info=False, uses_envs=False)
def standard_unit():
"""
Static method. Return the analysis unit corresponding to the Standard
package.
"""
pass
std = Property(
Self.standard_unit.root.cast(T.CompilationUnit)
.body.cast(T.LibraryItem).item.as_bare_entity,
doc="""
Retrieves the package corresponding to the Standard unit. Used to
access standard types.
"""
)
std_env = Property(
Self.std.children_env,
doc="Get the children env of the Standard package."
)
std_entity = Property(
lambda sym=Symbol: Self.unit.root.std_entity_implem(sym),
public=True,
doc="Static property. Return an entity from the standard package"
" with name `sym`."
)
std_entity_implem = Property(
lambda sym=Symbol: Self.std_env.get_first(sym, categories=noprims),
memoized=True
)
bool_type = Property(
Self.std_entity('Boolean'), public=True, doc="""
Static method. Return the standard Boolean type.
"""
)
int_type = Property(
Self.std_entity('Integer'), public=True, doc="""
Static method. Return the standard Integer type.
"""
)
universal_int_type = Property(
Self.std_entity('Universal_Int_Type_'), public=True, doc="""
Static method. Return the standard Universal Integer type.
"""
)
universal_real_type = Property(
Self.std_entity('Universal_Real_Type_'), public=True, doc="""
Static method. Return the standard Universal Real type.
"""
)
exc_id_type = Property(
Self
.get_unit_root_decl(['Ada', 'Exceptions'], UnitSpecification)
._.children_env.get_first('Exception_Id', lookup=LK.flat)
.cast(T.BaseTypeDecl), doc="""
Return the type Ada.Exceptions.Exception_Id.
"""
)
task_id_type = Property(
Self.get_unit_root_decl(['Ada', 'Task_Identification'],
UnitSpecification)
._.children_env.get_first('Task_Id', lookup=LK.flat)
.cast(T.BaseTypeDecl), doc="""
Return the type Ada.Task_Identification.Task_Id.
"""
)
@langkit_property(return_type=Bool)
def has_with_visibility(refd_unit=AnalysisUnit):
"""
Return whether Self's unit has "with visibility" on "refd_unit".
In other words, whether Self's unit has a WITH clause on "refd_unit",
or if its spec, or one of its parent specs has one.
"""
return Or(
refd_unit.is_referenced_from(Self.unit),
Self.parent_unit_env(
# Here we go and explicitly grab the top level item, rather
# than use Self's children env, because of use clauses, that
# can be at the top level but semantically belong to the env of
# the top level item.
Self.top_level_decl(Self.unit).children_env
)
.env_node._.has_with_visibility(refd_unit)
)
@langkit_property(return_type=Bool)
def has_visibility(other_entity=T.AdaNode.entity):
return Or(
# The node is a generic package instantiation coming from a formal
# package.
other_entity.cast(GenericPackageInstantiation)._.info.from_rebound,
# The node is not an unit root
Not(other_entity.cast(T.BasicDecl).is_unit_root),
# Else, check with visibility
Self.has_with_visibility(other_entity.node.unit)
)
@langkit_property()
def resolve_generic_actual():
"""
Helper property to resolve the actuals of generic instantiations.
"""
return Entity.match(
lambda te=T.TypeExpr.entity: bind_origin(Self, te.designated_type),
# TODO: depending on the formal that matches this actual, this name
# can be both an object or a type. For now, we assume it's a type
# but we should handle objects too.
lambda n=T.Name.entity: n.name_designated_type.cast(T.entity)._or(
# If we don't find a type, find something else
env.bind(n.children_env, n.env_elements.at(0))
),
lambda _: No(T.entity),
)
@langkit_property()
def top_level_use_package_clauses():
"""
If Self is a library item or a subunit, return a flat list of all names
for top-level UsePackageClause nodes. See
UsePackageClause.env_spec.ref_envs for more details.
"""
return (
Self.parent.parent.cast_or_raise(T.CompilationUnit)
.prelude
.filter(lambda p: p.is_a(UsePackageClause))
.mapcat(
lambda p: p.cast_or_raise(UsePackageClause).packages.map(
lambda n: n.cast(AdaNode)
)
)
)
@langkit_property()
def use_packages_in_spec_of_subp_body():
"""
If Self is a library-level SubpBody, fetch the environments USE'd in
its declaration.
"""
return Let(lambda subpb=Self.cast(T.SubpBody): If(
subpb.parent.is_a(T.LibraryItem),
subpb.as_bare_entity.decl_part.then(
lambda subp_decl: subp_decl.top_level_use_package_clauses.map(
lambda use_name:
bind_origin(use_name, env.bind(
use_name.node_env,
use_name.cast_or_raise(T.Name)
.as_bare_entity.designated_env
))
).env_group(),
default_val=EmptyEnv
),
EmptyEnv
))
@langkit_property()
def nested_generic_formal_part():
"""
Assuming Self is a generic entity's body that is nested (not a library
item), return the lexical environment for the corresponding
GenericPackageDecl (or GenericSubpDecl) node. Return an empty
environment in all other cases.
This is a helper for generic formals visibility in generic bodies. See
the use in the child_unit macro.
The following property is evaluated each time we make a recursive
lexical environment lookup on a child unit. As it does itself a lot of
lookups, memoizing it is very important.
"""
gen_decl = Var(Self.as_bare_entity.match(
lambda pkg_body=T.PackageBody:
pkg_body.decl_part.then(
lambda d: d.node.parent.cast(T.GenericPackageDecl)
),
lambda bod=T.BaseSubpBody:
# We're only searching for generics. We look at index 1 and
# 2, because if self is a subunit, the first entity we find
# will be the separate declaration. NOTE: We don't use
# decl_part/previous_part on purpose: They can cause env
# lookups, hence doing an infinite recursion.
bod.children_env.env_parent.get(
bod.name_symbol, categories=noprims
).then(
lambda results:
results.at(1).node.cast(T.GenericSubpDecl)._or(
results.at(2).node.cast(T.GenericSubpDecl)
)
).cast(T.AdaNode),
lambda _: No(T.AdaNode)
))
return gen_decl.then(
lambda gd: gd.children_env, default_val=Self.empty_env
)
@langkit_property()
def is_package():
"""
Property helper to determine if an entity is a package or not.
"""
return Self.is_a(PackageDecl, PackageBody, GenericPackageInstantiation,
PackageRenamingDecl, GenericPackageDecl)
@langkit_property()
def initial_env():
"""
Provide a lexical environment to use in EnvSpec's initial_env.
"""
return Self.parent.then(lambda p: p.children_env,
default_val=Self.children_env)
@langkit_property(ignore_warn_on_node=True, public=True)
def top_level_decl(unit=AnalysisUnit):
"""
Static method. Get the top-level decl in "unit".
This is the body of a Subunit, or the item of a LibraryItem.
"""
return unit._.root.then(
lambda root:
root.cast_or_raise(T.CompilationUnit).body.match(
lambda li=T.LibraryItem: li.item,
lambda su=T.Subunit: su.body,
lambda _: No(T.BasicDecl),
)
)
@langkit_property()
def unpack_formals(formal_params=T.BaseFormalParamDecl.entity.array):
"""
Static method. Couples (identifier, param spec) for all parameters.
"""
return Self.unit.root.unpack_formals_impl(formal_params)
@langkit_property()
def unpack_formals_impl(formal_params=T.BaseFormalParamDecl.entity.array):
return formal_params.mapcat(
lambda spec: spec.identifiers.map(lambda id: SingleFormal.new(
name=id, spec=spec
))
)
@langkit_property(return_type=T.ParamMatch.array)
def match_formals(formal_params=T.BaseFormalParamDecl.entity.array,
params=T.AssocList,
is_dottable_subp=Bool):
"""
For each ParamAssoc in a AssocList, return whether we could find a
matching formal in Self, and whether this formal is optional (i.e. has
a default value).
"""
def matches(formal, actual):
return ParamMatch.new(has_matched=True,
formal=formal, actual=actual)
unpacked_formals = Var(Self.unpack_formals(formal_params))
return params.then(lambda p: p.unpacked_params.map(lambda i, a: If(
a.name.is_null,
Let(lambda idx=If(is_dottable_subp, i + 1, i):
# Positional parameter case: if this parameter has no
# name association, make sure we have enough formals.
unpacked_formals.at(idx).then(lambda sp: matches(sp, a))),
# Named parameter case: make sure the designator is
# actualy a name and that there is a corresponding
# formal.
a.name.then(lambda id: (
unpacked_formals.find(lambda p: p.name.matches(id)).then(
lambda sp: matches(sp, a)
)
))
)))
@langkit_property(public=True, dynamic_vars=[default_imprecise_fallback()])
def gnat_xref():
"""
Return a cross reference from this name to a defining identifier,
trying to mimic GNAT's xrefs as much as possible.
"""
bd = Var(Entity.cast(T.Name).enclosing_defining_name
.then(lambda dn: dn.basic_decl))
return Cond(
bd.then(lambda bd: bd.is_a(T.ParamSpec))
& bd.semantic_parent.is_a(T.SubpDecl, T.ExprFunction,
T.GenericSubpInternal,
T.BaseTypeDecl),
bd.semantic_parent.cast(T.BasicDecl).defining_name,
bd.then(lambda bd: bd.is_a(T.DiscriminantSpec)),
bd.semantic_parent.cast(T.BasicDecl).defining_name,
bd.then(lambda bd: bd.is_a(T.ParamSpec))
& bd.semantic_parent.is_a(T.AbstractSubpDecl, T.FormalSubpDecl,
T.NullSubpDecl),
bd.semantic_parent.cast(T.BasicDecl).defining_name,
bd.then(lambda bd: bd.is_a(T.AbstractSubpDecl)),
bd.cast(T.AbstractSubpDecl).subp_decl_spec
.primitive_subp_of.defining_name,
bd.then(lambda bd: bd.is_a(T.BasicSubpDecl)),
bd.cast(T.BasicSubpDecl).subp_decl_spec.primitive_subp_of.then(
lambda prim_typ:
prim_typ.is_tagged_type.then(
lambda _: prim_typ.private_completion.then(
lambda pc: pc.defining_name
)._or(prim_typ.defining_name)
)
),
Let(lambda ret=Entity.xref: ret.then(
lambda _:
Let(lambda dbd=ret.basic_decl: Cond(
dbd.is_a(T.ParamSpec),
dbd.cast(T.ParamSpec).decl_param(ret),
dbd.is_a(T.GenericSubpInternal, T.GenericPackageInternal),
dbd.generic_instantiations.at(0).then(
lambda gi: gi.cast_or_raise(T.BasicDecl).defining_name,
default_val=ret
),
dbd.is_a(T.ObjectDecl),
dbd.cast(T.ObjectDecl).public_part_decl.then(
lambda ppd: ppd.defining_name
)._or(ret),
dbd.is_a(T.BaseSubpBody),
dbd.cast(T.BaseSubpBody)
.decl_part._or(dbd).defining_name,
ret
))
))
)
@langkit_property(return_type=T.AdaNode, ignore_warn_on_node=True)
def env_get_real_from_node(from_node=T.AdaNode):
"""
Static property. Finds the closest BasicSubpDecl /
GenericInstantiation. Is used by env_get and env_get_first wrappers to
refine from_node. The reason is that inside a declaration named D,
one can refer to previous declarations named D. But an env lookup
from a node inside D would return that D itself, not previous
declarations.
"""
return If(from_node.is_null, from_node, Let(
lambda c=from_node.parents.find(
lambda n: n.is_a(T.BasicSubpDecl, T.GenericInstantiation)
): If(c.is_null, from_node, c)
))
def child_unit(name_expr, scope_expr, dest_env=None,
transitive_parent=False, more_rules=[]):
"""
This macro will add the properties and the env specification necessary
to make a node implement the specification of a library child unit in
Ada, so that you can declare new childs to an unit outside of its own
scope.
:param AbstractExpression name_expr: The expression that will retrieve
the name symbol for the decorated node.
:param AbstractExpression scope_expr: The expression that will retrieve the
scope node for the decorated node. If the scope node is not found, it
should return EmptyEnv: in this case, the actual scope will become the
root environment.
:rtype: EnvSpec
"""
more_rules = list(more_rules)
add_to_env_expr = (
add_to_env_kv(name_expr, Self, dest_env=dest_env)
if dest_env else add_to_env_kv(name_expr, Self)
)
return EnvSpec(
call_env_hook(Self),
set_initial_env(
env.bind(Self.initial_env, Let(
lambda scope=scope_expr: If(scope == EmptyEnv, env, scope)
))
),
add_to_env_expr,
add_env(transitive_parent=transitive_parent),
ref_used_packages(),
ref_generic_formals(),
*more_rules