Skip to content

Commit 2580460

Browse files
committed
Introduce the named environments mechanism
See the "Named environments" comments section in pkg_implementation_spec.mako for details. TN: T320-010
1 parent b2f7a7d commit 2580460

15 files changed

+2114
-164
lines changed

langkit/compile_context.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,9 +1015,11 @@ def compute_types(self):
10151015
for n in self.grammar.user_defined_rules],
10161016
is_builtin_type=True)
10171017

1018-
# Force the creation of the env assoc type, as required by the
1018+
# Force the creation of the env assoc type and the
1019+
# Symbol.array/Symbol.array.array types, as required by the
10191020
# always-emitted PLE helpers.
1020-
_ = resolve_type(T.env_assoc)
1021+
for t in (T.env_assoc, T.Symbol.array, T.Symbol.array.array):
1022+
_ = resolve_type(t)
10211023

10221024
# Now that all types are known, construct default values for fields
10231025
for st in CompiledTypeRepo.struct_types:

langkit/envs.py

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ class MyNode(RootNode):
3737
# Public API for env actions
3838

3939
def add_env(no_parent: bool = False,
40-
transitive_parent: bool = False) -> AddEnv:
40+
transitive_parent: bool = False,
41+
names: Optional[AbstractExpression] = None) -> AddEnv:
4142
"""
4243
Add an environment linked to the current node. This env action must be
4344
called as a pre action. The only actions that can precede this one in pre
@@ -46,8 +47,12 @@ def add_env(no_parent: bool = False,
4647
4748
:param no_parent: If passed, the new env will be created with no parent
4849
env.
50+
:param transitive_parent: TODO.
51+
:param names: Optional list of names for the created environment. If not
52+
passed or if this is an empty array, the created environment is not
53+
named.
4954
"""
50-
return AddEnv(no_parent, transitive_parent)
55+
return AddEnv(no_parent, transitive_parent, names)
5156

5257

5358
class RefKind(Enum):
@@ -178,7 +183,28 @@ def set_initial_env(env_expr: AbstractExpression,
178183
:param unsound: Whether ``env_expr`` is allowed to return foreign
179184
environments.
180185
"""
181-
return SetInitialEnv(env_expr, unsound)
186+
return SetInitialEnv(None, env_expr, unsound)
187+
188+
189+
def set_initial_env_by_name(
190+
name_expr: AbstractExpression,
191+
fallback_env_expr: AbstractExpression
192+
) -> SetInitialEnv:
193+
"""
194+
Action that sets the initial env in which the rest of the environment
195+
actions are evaluated. Except for Do() hooks, this action must be first in
196+
the list of action (if present).
197+
198+
:param name_expr: Expression that returns an array of symbols (an env
199+
name). If it evaluates to a non-empty array, look for the environment
200+
that has this name (it will be updated every time another environment
201+
related to this name takes precedence). If it evaluates to an empty
202+
array, use ``fallback_env_expr`` to get the initial environment.
203+
:param fallback_env_expr: Expression that returns the initial env if there
204+
is no named env lookup. Note that except if it's the empty env or the
205+
root scope, this environment must not be foreign to Self.
206+
"""
207+
return SetInitialEnv(name_expr, fallback_env_expr)
182208

183209

184210
def do(expr: AbstractExpression) -> Do:
@@ -238,9 +264,14 @@ def count(cls: Type[EnvAction], sequence: List[EnvAction]) -> int:
238264
while actions and isinstance(actions[0], Do):
239265
first_actions.append(actions.pop(0))
240266

241-
# After that, allow exactly one call to SetInitialEnv
267+
# After that, allow at most one call to SetInitialEnv
242268
self.initial_env = None
243269
if actions and isinstance(actions[0], SetInitialEnv):
270+
check_source_language(
271+
isinstance(actions[0], SetInitialEnv),
272+
'The initial environment must come first after the potential'
273+
' do()'
274+
)
244275
self.initial_env = cast(SetInitialEnv, actions.pop(0))
245276
first_actions.append(self.initial_env)
246277
check_source_language(
@@ -379,13 +410,23 @@ def _render_field_access(self, p: PropertyDef) -> str:
379410
Self.construct_nocheck(), p, []
380411
).render_expr()
381412

413+
@property
414+
def initial_env_name_expr(self) -> str:
415+
"""
416+
The initial environment name expression.
417+
"""
418+
assert self.initial_env
419+
assert self.initial_env.name_expr
420+
assert self.initial_env.name_prop
421+
return self._render_field_access(self.initial_env.name_prop)
422+
382423
@property
383424
def initial_env_expr(self) -> str:
384425
"""
385426
The initial environment expression.
386427
"""
387428
assert self.initial_env
388-
return self._render_field_access(self.initial_env.initial_env_prop)
429+
return self._render_field_access(self.initial_env.fallback_env_prop)
389430

390431

391432
class EnvAction:
@@ -434,15 +475,20 @@ def rewrite_property_refs(self,
434475
class AddEnv(EnvAction):
435476
def __init__(self,
436477
no_parent: bool = False,
437-
transitive_parent: bool = False) -> None:
478+
transitive_parent: bool = False,
479+
names: Optional[AbstractExpression] = None) -> None:
438480
super().__init__()
439481
self.no_parent = no_parent
440482
self.transitive_parent = transitive_parent
483+
self.names = names
441484

442485
def create_internal_properties(self, env_spec: EnvSpec) -> None:
443486
self.transitive_parent_prop = env_spec.create_internal_property(
444487
'Env_Trans_Parent', unsugar(self.transitive_parent), T.Bool
445488
)
489+
self.names_prop = env_spec.create_internal_property(
490+
'Env_Names', self.names, T.Symbol.array.array
491+
)
446492

447493

448494
class AddToEnv(EnvAction):
@@ -619,26 +665,30 @@ class HandleChildren(EnvAction):
619665
pass
620666

621667

622-
class ExprHolderAction(EnvAction):
623-
def __init__(self, env_expr: AbstractExpression) -> None:
668+
class SetInitialEnv(EnvAction):
669+
def __init__(self,
670+
name_expr: Optional[AbstractExpression],
671+
fallback_env_expr: AbstractExpression,
672+
unsound: bool = False) -> None:
624673
super().__init__()
625-
self.env_expr = env_expr
626-
627-
628-
class SetInitialEnv(ExprHolderAction):
629-
630-
def __init__(self, env_expr: AbstractExpression, unsound: bool) -> None:
631-
super().__init__(env_expr)
674+
self.name_expr = name_expr
675+
self.fallback_env_expr = fallback_env_expr
632676
self.unsound = unsound
633677

634678
def create_internal_properties(self, env_spec: EnvSpec) -> None:
635-
self.initial_env_prop = env_spec.create_internal_property(
636-
'Initial_Env', self.env_expr, T.LexicalEnv
679+
self.name_prop = env_spec.create_internal_property(
680+
'Initial_Env_Name', self.name_expr, T.Symbol.array
681+
)
682+
self.fallback_env_prop = env_spec.create_internal_property(
683+
'Initial_Env', self.fallback_env_expr, T.LexicalEnv
637684
)
638685

639686

640-
class Do(ExprHolderAction):
641-
def create_internal_properties(self, env_spec: EnvSpec) -> None:
642-
self.do_property = env_spec.create_internal_property(
643-
'Env_Do', self.env_expr, None
687+
class Do(EnvAction):
688+
def __init__(self, expr: AbstractExpression) -> None:
689+
self.expr = expr
690+
691+
def create_internal_properties(self, spec: EnvSpec) -> None:
692+
self.do_property = spec.create_internal_property(
693+
'Env_Do', self.expr, None
644694
)

langkit/gdb/printers.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
import gdb
88
import gdb.printing
9+
from gnatdbg.strings import StringAccess
910

1011
from langkit.gdb.tdh import TDH
1112
from langkit.gdb.units import AnalysisUnit
1213
from langkit.gdb.utils import adaify_name
1314
from langkit.utils import memoized
1415

16+
1517
if TYPE_CHECKING:
1618
from langkit.gdb.context import Context
1719

@@ -333,6 +335,57 @@ def to_string(self):
333335
return '<LexicalEnv (corrupted)]'
334336

335337

338+
class EnvNamePrinter(BasePrinter):
339+
"""
340+
Pretty-printer for env names.
341+
"""
342+
name = 'EnvName'
343+
344+
matcher = RecordAccessMatcher('env_name_record', 'env_name')
345+
346+
@classmethod
347+
def matches(cls, value, context):
348+
return cls.matcher.matches_access(value, context)
349+
350+
def to_string(self):
351+
if not self.value:
352+
return 'null'
353+
354+
# Since GDB's Python API only exposes the low-level details of how Ada
355+
# arrays are implemented, we need to do some pointer arithmetic in
356+
# order to fetch the sequence of symbols that this EnvName contains::
357+
#
358+
# $.Symbols (1 .. $.Size)
359+
360+
# Lookup the symbol type (array element)
361+
symbol_type = gdb.lookup_type(
362+
self.context.comname('symbols__symbol_type')
363+
)
364+
symbol_ptr = symbol_type.pointer()
365+
366+
# Fetch the size and the address of the first array element
367+
size = int(self.value['size'])
368+
symbols_addr = int(self.value['symbols'].address)
369+
370+
# We can now fetch individual symbols
371+
result = []
372+
for i in range(size):
373+
sym_addr = symbols_addr + i * symbol_type.sizeof
374+
sym = gdb.Value(sym_addr).cast(symbol_ptr).dereference()
375+
376+
# Fetch the text corresponding to the current symbol. Symbols are
377+
# implemented as wide wide strings, so decode them using the UTF-32
378+
# encoding (native endianity). Do not crash on
379+
# uninitialized/corrupted symbol: try to be as helpful as possible.
380+
try:
381+
sym_text = StringAccess(sym).get_string(encoding='utf-32')
382+
except gdb.error:
383+
sym_text = '???'
384+
385+
result.append(sym_text)
386+
return 'EnvName({})'.format('.'.join(result))
387+
388+
336389
class LexicalEnvPrinter(BasePrinter):
337390
"""
338391
Pretty-printer for lexical environments.

langkit/gdb/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def setup(lib_name, astnode_names, astnode_kinds, prefix):
3030
for printer in [
3131
printers.AnalysisUnitPrinter,
3232
printers.ASTNodePrinter,
33+
printers.EnvNamePrinter,
3334
printers.LexicalEnvPrinter,
3435
printers.EnvGetterPrinter,
3536
printers.ReferencedEnvPrinter,

langkit/templates/astnode_types_ada.mako

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,13 @@
211211
% if cls.env_spec.pre_actions:
212212
procedure ${cls.raw_name}_Pre_Env_Actions
213213
(Self : ${cls.name};
214-
State : in out PLE_State;
214+
State : in out PLE_Node_State;
215215
Add_To_Env_Only : Boolean := False);
216216
% endif
217217
218218
% if cls.env_spec.post_actions:
219219
procedure ${cls.raw_name}_Post_Env_Actions
220-
(Self : ${cls.name}; State : in out PLE_State);
220+
(Self : ${cls.name}; State : in out PLE_Node_State);
221221
% endif
222222
223223
% endif
@@ -283,9 +283,15 @@
283283
env_getter = "{}_Initial_Env_Getter_Fn".format(cls.name)
284284
%>
285285
286-
<%def name="emit_set_initial_env(do_action)">
287-
State.Current_Env := ${env_getter}
288-
((Node => Self, Info => ${T.entity_info.nullexpr}));
286+
<%def name="emit_set_initial_env(sie)">
287+
declare
288+
Name_Result : ${T.Symbol.array.name} :=
289+
${(cls.env_spec.initial_env_name_expr
290+
if sie.name_prop
291+
else 'null')};
292+
begin
293+
Set_Initial_Env (Self, State, Name_Result, ${env_getter}'Access);
294+
end;
289295
</%def>
290296
291297
<%def name="emit_add_to_env(exprs)">
@@ -315,7 +321,7 @@
315321
% endif
316322
317323
Add_To_Env
318-
(Self, Mapping, State.Current_Env, Resolver,
324+
(Self, Mapping, State, Resolver,
319325
DSL_Location => ${('""'
320326
if exprs.unsound else
321327
string_repr(exprs.str_location))});
@@ -377,29 +383,16 @@
377383
end if;
378384
379385
declare
380-
G : Env_Getter :=
381-
% if add_env.no_parent:
382-
AST_Envs.No_Env_Getter
383-
% else:
384-
AST_Envs.Simple_Env_Getter (State.Current_Env)
385-
% endif
386-
;
386+
No_Parent : constant Boolean :=
387+
${'True' if add_env.no_parent else 'False'};
388+
Transitive_Parent : constant Boolean :=
389+
${call_prop(add_env.transitive_parent_prop)};
390+
Resolver : constant Lexical_Env_Resolver :=
391+
${"{}'Access".format(env_getter) if has_dyn_env else 'null'};
392+
Names : ${T.Symbol.array.array.name} :=
393+
${call_prop(add_env.names_prop) if add_env.names_prop else 'null'};
387394
begin
388-
% if has_dyn_env:
389-
if State.Current_Env.Env.Node /= null
390-
and then State.Current_Env.Env.Node.Unit /= Self.Unit
391-
then
392-
G := AST_Envs.Dyn_Env_Getter (${env_getter}'Access, Self);
393-
end if;
394-
% endif
395-
396-
Self.Self_Env := AST_Envs.Create_Lexical_Env
397-
(Parent => G,
398-
Node => Self,
399-
Transitive_Parent => ${call_prop(add_env.transitive_parent_prop)},
400-
Owner => Self.Unit);
401-
State.Current_Env := Self.Self_Env;
402-
Register_Destroyable (Self.Unit, Self.Self_Env.Env);
395+
Add_Env (Self, State, No_Parent, Transitive_Parent, Resolver, Names);
403396
end;
404397
</%def>
405398
@@ -447,24 +440,23 @@
447440
448441
Initial_Env : Lexical_Env := Bound_Env;
449442
begin
450-
% if cls.env_spec.initial_env:
451-
Initial_Env := ${cls.env_spec.initial_env_expr};
443+
Initial_Env := ${cls.env_spec.initial_env_expr};
452444
453-
if Initial_Env.Kind /= Primary then
445+
if Initial_Env.Kind /= Primary then
446+
raise Property_Error with
447+
"Cannot set initial env to non-primary one";
448+
end if;
449+
450+
% if not cls.env_spec.initial_env.unsound:
451+
if Initial_Env.Env.Node /= null
452+
and then Initial_Env.Env.Node.Unit /= Self.Unit
453+
then
454454
raise Property_Error with
455-
"Cannot set initial env to non-primary one";
455+
"unsound foreign environment in SetInitialEnv ("
456+
& "${cls.env_spec.initial_env.str_location}" & ")";
456457
end if;
457-
458-
% if not cls.env_spec.initial_env.unsound:
459-
if Initial_Env.Env.Node /= null
460-
and then Initial_Env.Env.Node.Unit /= Self.Unit
461-
then
462-
raise Property_Error with
463-
"unsound foreign environment in SetInitialEnv ("
464-
& "${cls.env_spec.initial_env.str_location}" & ")";
465-
end if;
466-
% endif
467458
% endif
459+
468460
return Initial_Env;
469461
end ${env_getter};
470462
@@ -475,7 +467,7 @@
475467
% if cls.env_spec.pre_actions:
476468
procedure ${cls.raw_name}_Pre_Env_Actions
477469
(Self : ${cls.name};
478-
State : in out PLE_State;
470+
State : in out PLE_Node_State;
479471
Add_To_Env_Only : Boolean := False) is
480472
begin
481473
% for action in cls.env_spec.pre_actions:
@@ -486,7 +478,7 @@
486478
487479
% if cls.env_spec.post_actions:
488480
procedure ${cls.raw_name}_Post_Env_Actions
489-
(Self : ${cls.name}; State : in out PLE_State) is
481+
(Self : ${cls.name}; State : in out PLE_Node_State) is
490482
begin
491483
% for action in cls.env_spec.post_actions:
492484
${emit_env_action (action)}

0 commit comments

Comments
 (0)