resolve_lang/policy.rs
1//! The scoping policy: how one language binds names, as data.
2//!
3//! Every forged language reaches resolve-lang as HIR, so the differences
4//! between, say, PHP, Python, and Kraken are not separate resolvers but
5//! settings: when an item becomes visible (hoisting), whether a binder may
6//! reuse a visible name (shadowing), which namespaces exist and which share a
7//! table (merging), what a method body can see of its class, how the
8//! late-static-binding roots bind, and how imports and visibility behave.
9
10use hir_lang::{ItemKind, Vis};
11
12/// One of the language-level namespaces a name can live in.
13///
14/// HIR paths carry a syntactic namespace ([`hir_lang::Ns`]); these are the
15/// *tables* names are stored in. A language merges tables with
16/// [`Policy::with_merge`] (Python keeps everything in one table; Rust keeps types
17/// and modules in one; by default constants share the value table, PHP keeps
18/// them apart).
19///
20/// # Examples
21///
22/// ```
23/// use resolve_lang::Namespace;
24///
25/// assert_eq!(Namespace::Value.name(), "value");
26/// assert_eq!(Namespace::ALL.len(), 6);
27/// ```
28#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub enum Namespace {
30 /// Functions, constants, globals, constructors.
31 Value,
32 /// Records, sums, classes, interfaces, aliases.
33 Type,
34 /// Modules and units.
35 Module,
36 /// Macros. HIR has no macro items (macros expand before lowering); hosts
37 /// may still export names here through [`Env`](crate::Env).
38 Macro,
39 /// Loop and block labels. Labels are binders resolved by lowering, so no
40 /// HIR path is looked up here; the namespace exists for merging rules.
41 Label,
42 /// Constants (and, under [`Policy::php`], globals and static properties):
43 /// the names a language keeps apart from its functions. Merged into
44 /// [`Value`](Self::Value) unless the policy separates it, as PHP does: a
45 /// PHP function and a constant (or a method and a class constant) may
46 /// share a name. A value path that is the callee of a call searches the
47 /// value table first and this one second; any other value path searches
48 /// this one first.
49 Const,
50}
51
52impl Namespace {
53 /// Every namespace, in table order.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// use resolve_lang::Namespace;
59 ///
60 /// assert_eq!(Namespace::ALL[0], Namespace::Value);
61 /// ```
62 pub const ALL: [Self; 6] = [
63 Self::Value,
64 Self::Type,
65 Self::Module,
66 Self::Macro,
67 Self::Label,
68 Self::Const,
69 ];
70
71 /// The lowercase name, for messages.
72 ///
73 /// # Examples
74 ///
75 /// ```
76 /// use resolve_lang::Namespace;
77 ///
78 /// assert_eq!(Namespace::Module.name(), "module");
79 /// ```
80 #[must_use]
81 pub const fn name(self) -> &'static str {
82 match self {
83 Self::Value => "value",
84 Self::Type => "type",
85 Self::Module => "module",
86 Self::Macro => "macro",
87 Self::Label => "label",
88 Self::Const => "constant",
89 }
90 }
91
92 #[inline]
93 pub(crate) const fn index(self) -> usize {
94 self as usize
95 }
96
97 pub(crate) const fn from_index(i: usize) -> Self {
98 match i {
99 0 => Self::Value,
100 1 => Self::Type,
101 2 => Self::Module,
102 3 => Self::Macro,
103 4 => Self::Label,
104 _ => Self::Const,
105 }
106 }
107}
108
109/// A set of [`Namespace`]s, as a bit set.
110///
111/// # Examples
112///
113/// ```
114/// use resolve_lang::{Namespace, NsSet};
115///
116/// let set = NsSet::of(&[Namespace::Type, Namespace::Value]);
117/// assert!(set.contains(Namespace::Type));
118/// assert!(!set.contains(Namespace::Module));
119/// assert_eq!(set.iter().count(), 2);
120/// ```
121#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
122pub struct NsSet(u8);
123
124impl NsSet {
125 /// The empty set.
126 ///
127 /// # Examples
128 ///
129 /// ```
130 /// use resolve_lang::NsSet;
131 ///
132 /// assert!(NsSet::EMPTY.is_empty());
133 /// ```
134 pub const EMPTY: Self = Self(0);
135
136 /// A set holding exactly `namespaces`.
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use resolve_lang::{Namespace, NsSet};
142 ///
143 /// assert_eq!(NsSet::of(&[Namespace::Value]), NsSet::single(Namespace::Value));
144 /// ```
145 #[must_use]
146 pub const fn of(namespaces: &[Namespace]) -> Self {
147 let mut bits = 0u8;
148 let mut i = 0;
149 while i < namespaces.len() {
150 bits |= 1 << namespaces[i] as u8;
151 i += 1;
152 }
153 Self(bits)
154 }
155
156 /// A set holding one namespace.
157 ///
158 /// # Examples
159 ///
160 /// ```
161 /// use resolve_lang::{Namespace, NsSet};
162 ///
163 /// assert!(NsSet::single(Namespace::Macro).contains(Namespace::Macro));
164 /// ```
165 #[must_use]
166 pub const fn single(ns: Namespace) -> Self {
167 Self(1 << ns as u8)
168 }
169
170 /// Whether `ns` is in the set.
171 ///
172 /// # Examples
173 ///
174 /// ```
175 /// use resolve_lang::{Namespace, NsSet};
176 ///
177 /// assert!(!NsSet::EMPTY.contains(Namespace::Value));
178 /// ```
179 #[must_use]
180 pub const fn contains(self, ns: Namespace) -> bool {
181 self.0 & (1 << ns as u8) != 0
182 }
183
184 /// Whether the set is empty.
185 ///
186 /// # Examples
187 ///
188 /// ```
189 /// use resolve_lang::{Namespace, NsSet};
190 ///
191 /// assert!(!NsSet::single(Namespace::Type).is_empty());
192 /// ```
193 #[must_use]
194 pub const fn is_empty(self) -> bool {
195 self.0 == 0
196 }
197
198 /// The set with `ns` added.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use resolve_lang::{Namespace, NsSet};
204 ///
205 /// let s = NsSet::EMPTY.with(Namespace::Type);
206 /// assert!(s.contains(Namespace::Type));
207 /// ```
208 #[must_use]
209 pub const fn with(self, ns: Namespace) -> Self {
210 Self(self.0 | (1 << ns as u8))
211 }
212
213 /// The namespaces in the set, in table order.
214 ///
215 /// # Examples
216 ///
217 /// ```
218 /// use resolve_lang::{Namespace, NsSet};
219 ///
220 /// let v: Vec<_> = NsSet::of(&[Namespace::Module, Namespace::Value]).iter().collect();
221 /// assert_eq!(v, [Namespace::Value, Namespace::Module]);
222 /// ```
223 pub fn iter(self) -> impl Iterator<Item = Namespace> {
224 Namespace::ALL
225 .into_iter()
226 .filter(move |ns| self.contains(*ns))
227 }
228}
229
230/// The kinds of named items a policy distinguishes.
231///
232/// # Examples
233///
234/// ```
235/// use resolve_lang::ItemClass;
236///
237/// assert_eq!(ItemClass::ALL.len(), 11);
238/// ```
239#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
240pub enum ItemClass {
241 /// A function or method.
242 Fn,
243 /// A record (struct).
244 Record,
245 /// A sum (enum).
246 Sum,
247 /// A class (or PHP trait, a mixin class).
248 Class,
249 /// An interface or trait.
250 Interface,
251 /// A type alias.
252 Alias,
253 /// An associated type.
254 AssocType,
255 /// A constant.
256 Const,
257 /// A global variable (a static, a Python module-level variable).
258 Global,
259 /// A module.
260 Module,
261 /// An import (`use`, `import`, `from … import`).
262 Import,
263}
264
265impl ItemClass {
266 /// Every item class.
267 ///
268 /// # Examples
269 ///
270 /// ```
271 /// use resolve_lang::ItemClass;
272 ///
273 /// assert_eq!(ItemClass::ALL[0], ItemClass::Fn);
274 /// ```
275 pub const ALL: [Self; 11] = [
276 Self::Fn,
277 Self::Record,
278 Self::Sum,
279 Self::Class,
280 Self::Interface,
281 Self::Alias,
282 Self::AssocType,
283 Self::Const,
284 Self::Global,
285 Self::Module,
286 Self::Import,
287 ];
288
289 /// The class of a HIR item kind, or `None` for kinds that bind no name
290 /// (`impl`, mixin use, error items).
291 ///
292 /// # Examples
293 ///
294 /// ```
295 /// use hir_lang::{FnDef, ItemKind};
296 /// use resolve_lang::ItemClass;
297 ///
298 /// assert_eq!(ItemClass::of(&ItemKind::Fn(FnDef::default())), Some(ItemClass::Fn));
299 /// assert_eq!(ItemClass::of(&ItemKind::Err), None);
300 /// ```
301 #[must_use]
302 pub const fn of(kind: &ItemKind) -> Option<Self> {
303 Some(match kind {
304 ItemKind::Fn(_) => Self::Fn,
305 ItemKind::Record(_) => Self::Record,
306 ItemKind::Sum(_) => Self::Sum,
307 ItemKind::Class(_) => Self::Class,
308 ItemKind::Interface(_) => Self::Interface,
309 ItemKind::Alias { .. } => Self::Alias,
310 ItemKind::AssocType { .. } => Self::AssocType,
311 ItemKind::Const { .. } => Self::Const,
312 ItemKind::Global { .. } => Self::Global,
313 ItemKind::Module { .. } => Self::Module,
314 ItemKind::Import { .. } => Self::Import,
315 ItemKind::Impl(_) | ItemKind::MixinUse(_) | ItemKind::Err => return None,
316 })
317 }
318
319 #[inline]
320 const fn index(self) -> usize {
321 self as usize
322 }
323}
324
325/// When an item's name becomes visible in its scope.
326///
327/// # Examples
328///
329/// ```
330/// use resolve_lang::{Hoist, ItemClass, Policy};
331///
332/// // PHP: a function declared anywhere is visible in its whole namespace.
333/// assert_eq!(Policy::php().hoisting(ItemClass::Fn), Hoist::Module);
334/// ```
335#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
336pub enum Hoist {
337 /// Visible throughout the scope that declares it, before and after the
338 /// declaration (Rust and Kraken items, Python's per-scope binding rule).
339 Scope,
340 /// Visible from the declaration to the end of its scope, including inside
341 /// itself, so a function can recurse (C-style declare-before-use).
342 AfterDecl,
343 /// Visible throughout the nearest enclosing module, wherever it is
344 /// declared, even inside a function body or a conditional block (PHP
345 /// functions and classes).
346 Module,
347}
348
349/// Whether a binder may reuse a name that is already visible.
350///
351/// The rule is about binders a user declares. A place alias (the binder of a
352/// HIR `let_place`, which lowering introduces for a compound assignment) is
353/// exempt: it is never reported, and a local declared inside its body is not
354/// reported for reusing its name.
355///
356/// # Examples
357///
358/// ```
359/// use resolve_lang::{Policy, Shadowing};
360///
361/// let p = Policy::new().with_shadowing(Shadowing::DenySameScope);
362/// assert_eq!(p.shadowing(), Shadowing::DenySameScope);
363/// ```
364#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
365pub enum Shadowing {
366 /// Any binder may shadow any visible name (Rust `let`, Python, PHP).
367 Allow,
368 /// A binder may not reuse a name bound earlier in the *same* scope
369 /// (JavaScript `let`, a parameter list with a repeated name).
370 DenySameScope,
371 /// A binder may not reuse the name of any local binder visible from the
372 /// same frame (C#, Zig).
373 DenyLocals,
374}
375
376/// What two definitions of one name in one scope and namespace mean.
377///
378/// # Examples
379///
380/// ```
381/// use resolve_lang::{Policy, Redefinition};
382///
383/// assert_eq!(Policy::python().redefinition(), Redefinition::LastWins);
384/// assert_eq!(Policy::kraken().redefinition(), Redefinition::Error);
385/// ```
386#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
387pub enum Redefinition {
388 /// A duplicate definition is an error; the first one is kept.
389 Error,
390 /// A later definition replaces an earlier one (Python `def f` twice).
391 /// With [`Hoist::AfterDecl`] each definition is visible from its own
392 /// declaration on.
393 LastWins,
394}
395
396/// What code nested in a class can see of the class's members unqualified.
397///
398/// # Examples
399///
400/// ```
401/// use resolve_lang::{ClassScope, Policy};
402///
403/// assert_eq!(Policy::python().class_scope(), ClassScope::BodyOnly);
404/// assert_eq!(Policy::php().class_scope(), ClassScope::Qualified);
405/// ```
406#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
407pub enum ClassScope {
408 /// Members are in lexical scope everywhere inside the class, methods
409 /// included (Java, C++, Kraken). Only the class's own members are seen
410 /// unqualified; inherited members go through `self::`/type-directed
411 /// lookup.
412 Lexical,
413 /// Members are visible to the class body's own initializers but not
414 /// inside methods, lambdas, or nested classes (Python).
415 BodyOnly,
416 /// Members are never visible unqualified; code writes `self::x`,
417 /// `static::x`, or `$this->x` (PHP).
418 Qualified,
419}
420
421/// Whether a nested module sees the names of the modules around it.
422///
423/// # Examples
424///
425/// ```
426/// use resolve_lang::{ModuleScope, Policy};
427///
428/// assert_eq!(Policy::kraken().module_scope(), ModuleScope::Isolated);
429/// ```
430#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
431pub enum ModuleScope {
432 /// A nested module starts a fresh lexical world: outer items are reached
433 /// through `super::` or an import (Rust, Kraken).
434 Isolated,
435 /// A nested module sees enclosing scopes' names (PHP namespaces falling
436 /// back outward, languages with lexically nested modules).
437 Lexical,
438}
439
440/// How a type-relative root (`self::`, `parent::`, `static::`) binds.
441///
442/// # Examples
443///
444/// ```
445/// use resolve_lang::{Policy, RootBinding};
446///
447/// // PHP: `static::` is late static binding, resolved at run time.
448/// assert_eq!(Policy::php().static_root(), RootBinding::Late);
449/// assert_eq!(Policy::php().self_root(), RootBinding::Early);
450/// ```
451#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
452pub enum RootBinding {
453 /// Bound at resolution time against the enclosing class (or its first
454 /// base, for `parent::`): a member found there is resolved now; one not
455 /// found is left to type-directed resolution.
456 Early,
457 /// Bound late (at run time or by the type checker): the whole path is
458 /// left type-directed (`unresolved = segments`).
459 Late,
460 /// The language has no such root; a path using it is reported.
461 Unsupported,
462}
463
464/// How an import's own visibility affects re-export.
465///
466/// # Examples
467///
468/// ```
469/// use resolve_lang::{Policy, Reexport};
470///
471/// assert_eq!(Policy::python().reexport(), Reexport::Always);
472/// ```
473#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
474pub enum Reexport {
475 /// An import is visible to other modules exactly as its item's [`Vis`]
476 /// says (`pub use` re-exports, plain `use` does not).
477 AsDeclared,
478 /// Every import becomes part of the module's exports (Python: an imported
479 /// name is a module attribute).
480 Always,
481 /// Imports are never exported (PHP: `use` is file-local).
482 Never,
483}
484
485/// How a namespace's table compares names.
486///
487/// Folding is a property of the *table*: every name stored in it, and every
488/// name looked up in it, is compared through the same fold. Folding is
489/// deterministic and independent of locale; diagnostics, the index, and
490/// [`ClassMember`](crate::ClassMember) keep each name as it was written.
491///
492/// Only ASCII folding is offered: it is what PHP 8 does for function,
493/// class, interface, trait, enum, and namespace names (bytes `A`-`Z` match
494/// `a`-`z`; every other byte, including UTF-8 sequences, must match
495/// exactly). Unicode case folding is not provided: no target language needs
496/// it, and its tables change with the Unicode version, which would make
497/// resolution depend on the toolchain.
498///
499/// # Examples
500///
501/// ```
502/// use resolve_lang::{Case, Namespace, Policy};
503///
504/// assert_eq!(Policy::php().case(Namespace::Value), Case::AsciiInsensitive);
505/// assert_eq!(Policy::php().case(Namespace::Const), Case::Sensitive);
506/// assert_eq!(Policy::python().case(Namespace::Value), Case::Sensitive);
507/// ```
508#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
509#[non_exhaustive]
510pub enum Case {
511 /// Names match only when spelled identically (the default everywhere).
512 Sensitive,
513 /// Names match when they are equal after mapping ASCII `A`-`Z` to
514 /// `a`-`z` (PHP 8).
515 AsciiInsensitive,
516}
517
518/// A language's complete scoping policy.
519///
520/// Start from a preset ([`kraken`](Self::kraken), [`php`](Self::php),
521/// [`python`](Self::python)) or [`new`](Self::new) (the lexical default, the
522/// same as Kraken), then adjust with the `with_*` setters. The policy is plain
523/// data, normally read from the language's sketch.
524///
525/// # Examples
526///
527/// ```
528/// use resolve_lang::{ClassScope, Hoist, ItemClass, Namespace, Policy};
529///
530/// let p = Policy::new()
531/// .with_hoisting(ItemClass::Fn, Hoist::AfterDecl)
532/// .with_class_scope(ClassScope::BodyOnly)
533/// .with_merge(Namespace::Type, Namespace::Value);
534/// assert_eq!(p.hoisting(ItemClass::Fn), Hoist::AfterDecl);
535/// assert_eq!(p.table(Namespace::Type), Namespace::Value);
536/// ```
537#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
538pub struct Policy {
539 hoist: [Hoist; 11],
540 occupies: [NsSet; 11],
541 merge: [Namespace; 6],
542 /// By table index (the namespace a table is merged into).
543 case: [Case; 6],
544 shadowing: Shadowing,
545 redefinition: Redefinition,
546 class_scope: ClassScope,
547 module_scope: ModuleScope,
548 roots: [RootBinding; 3],
549 enforce_visibility: bool,
550 private_to_descendants: bool,
551 globs: bool,
552 aliases: bool,
553 reexport: Reexport,
554 implicit_globals: bool,
555 class_values: bool,
556 interface_members: bool,
557}
558
559impl Default for Policy {
560 fn default() -> Self {
561 Self::new()
562 }
563}
564
565impl Policy {
566 /// The lexical default (equal to [`kraken`](Self::kraken)): every item
567 /// hoisted to its scope; separate value, type, and macro tables with
568 /// modules sharing the type table and constants sharing the value table;
569 /// every table case-sensitive; class members (own and inherited) visible
570 /// in methods;
571 /// isolated nested modules; `Self::` early, `parent::`/`static::`
572 /// unsupported; visibility enforced with private items visible to child
573 /// modules; globs, aliases, and declared re-exports.
574 ///
575 /// # Examples
576 ///
577 /// ```
578 /// use resolve_lang::{ItemClass, Namespace, NsSet, Policy};
579 ///
580 /// let p = Policy::new();
581 /// assert_eq!(p.occupies(ItemClass::Record), NsSet::of(&[Namespace::Type, Namespace::Value]));
582 /// assert!(p.visibility_enforced());
583 /// ```
584 #[must_use]
585 pub const fn new() -> Self {
586 use Namespace::{Const, Label, Macro, Module, Type, Value};
587 let value = NsSet::single(Value);
588 let ty = NsSet::single(Type);
589 let both = NsSet::of(&[Type, Value]);
590 Self {
591 hoist: [Hoist::Scope; 11],
592 // Fn Record Sum Class Interface Alias AssocType Const Global Module Import
593 occupies: [
594 value,
595 both,
596 ty,
597 both,
598 ty,
599 ty,
600 ty,
601 NsSet::single(Const),
602 value,
603 NsSet::single(Module),
604 NsSet::EMPTY,
605 ],
606 merge: [Value, Type, Type, Macro, Label, Value],
607 case: [Case::Sensitive; 6],
608 shadowing: Shadowing::Allow,
609 redefinition: Redefinition::Error,
610 class_scope: ClassScope::Lexical,
611 module_scope: ModuleScope::Isolated,
612 roots: [
613 RootBinding::Early,
614 RootBinding::Unsupported,
615 RootBinding::Unsupported,
616 ],
617 enforce_visibility: true,
618 private_to_descendants: true,
619 globs: true,
620 aliases: true,
621 reexport: Reexport::AsDeclared,
622 implicit_globals: false,
623 class_values: false,
624 interface_members: false,
625 }
626 }
627
628 /// Kraken and Iron: lexical scoping, modules with imports and
629 /// visibility. Equal to [`new`](Self::new).
630 ///
631 /// # Examples
632 ///
633 /// ```
634 /// use resolve_lang::Policy;
635 ///
636 /// assert_eq!(Policy::kraken(), Policy::new());
637 /// ```
638 #[must_use]
639 pub const fn kraken() -> Self {
640 Self::new()
641 }
642
643 /// PHP (and Mox): functions, classes, interfaces, enums, and constants are
644 /// visible in their whole namespace wherever declared; class members are
645 /// reached only through `self::`/`parent::`/`static::` (late static
646 /// binding for `static::`); `use` imports are file-local and have no
647 /// globs; `global $x` may name a global that does not exist yet.
648 ///
649 /// Names follow PHP 8's tables and case rules:
650 ///
651 /// | Table | Holds | Case |
652 /// |---|---|---|
653 /// | [`Value`](Namespace::Value) | functions, methods | ASCII-insensitive |
654 /// | [`Type`](Namespace::Type) (with [`Module`](Namespace::Module)) | classes, interfaces, traits, enums, namespaces | ASCII-insensitive |
655 /// | [`Const`](Namespace::Const) | constants, class constants, globals, static properties | sensitive |
656 ///
657 /// So a function and a constant, or a method and a class constant, may
658 /// share a name; `strlen` and `STRLEN` are one function; and a class
659 /// lives in the type table only. A class named where a value goes
660 /// (`new C`, `x instanceof C`, as the Mox sketch lowers them) still
661 /// resolves, through [`class_values`](Self::class_values); a class reaches
662 /// its interfaces' constants and methods through
663 /// [`interface_members`](Self::interface_members). HIR paths carry no `$` sigil, so a sketch that strips it from static
664 /// property names makes a static property and a class constant of the
665 /// same name collide; keep the sigil in property names (`$count`) to
666 /// keep them apart.
667 ///
668 /// # Examples
669 ///
670 /// ```
671 /// use resolve_lang::{Case, Hoist, ItemClass, Namespace, NsSet, Policy};
672 ///
673 /// let p = Policy::php();
674 /// assert_eq!(p.hoisting(ItemClass::Class), Hoist::Module);
675 /// assert_eq!(p.occupies(ItemClass::Class), NsSet::single(Namespace::Type));
676 /// assert_ne!(p.table(Namespace::Const), p.table(Namespace::Value));
677 /// assert_eq!(p.case(Namespace::Type), Case::AsciiInsensitive);
678 /// assert!(!p.globs_allowed());
679 /// assert!(p.implicit_globals());
680 /// assert!(p.class_values() && p.interface_members());
681 /// ```
682 #[must_use]
683 pub const fn php() -> Self {
684 let mut p = Self::new();
685 let mut i = 0;
686 while i < 11 {
687 let c = ItemClass::ALL[i];
688 if matches!(
689 c,
690 ItemClass::Fn
691 | ItemClass::Record
692 | ItemClass::Sum
693 | ItemClass::Class
694 | ItemClass::Interface
695 | ItemClass::Const
696 ) {
697 p.hoist[i] = Hoist::Module;
698 }
699 if matches!(
700 c,
701 ItemClass::Record | ItemClass::Sum | ItemClass::Class | ItemClass::Interface
702 ) {
703 p.occupies[i] = NsSet::single(Namespace::Type);
704 }
705 i += 1;
706 }
707 p.occupies[ItemClass::Global.index()] = NsSet::single(Namespace::Const);
708 p.merge = [
709 Namespace::Value,
710 Namespace::Type,
711 Namespace::Type,
712 Namespace::Macro,
713 Namespace::Label,
714 Namespace::Const,
715 ];
716 p.case[Namespace::Value.index()] = Case::AsciiInsensitive;
717 p.case[Namespace::Type.index()] = Case::AsciiInsensitive;
718 p.class_scope = ClassScope::Qualified;
719 p.module_scope = ModuleScope::Lexical;
720 p.roots = [RootBinding::Early, RootBinding::Early, RootBinding::Late];
721 p.globs = false;
722 p.reexport = Reexport::Never;
723 p.implicit_globals = true;
724 p.class_values = true;
725 p.interface_members = true;
726 p
727 }
728
729 /// Python (and Mercury): one case-sensitive table for every kind of
730 /// name; a name bound anywhere in a scope belongs to the whole scope;
731 /// class bodies are not visible to methods; members are inherited in C3
732 /// method resolution order (as under every policy; an inconsistent
733 /// hierarchy is reported, as Python raises `TypeError` at class
734 /// creation); redefinition rebinds; visibility is convention only;
735 /// imports become module attributes.
736 ///
737 /// # Examples
738 ///
739 /// ```
740 /// use resolve_lang::{Namespace, Policy};
741 ///
742 /// let p = Policy::python();
743 /// assert_eq!(p.table(Namespace::Type), Namespace::Value);
744 /// assert_eq!(p.table(Namespace::Module), Namespace::Value);
745 /// assert!(!p.visibility_enforced());
746 /// ```
747 #[must_use]
748 pub const fn python() -> Self {
749 let mut p = Self::new();
750 p.merge = [
751 Namespace::Value,
752 Namespace::Value,
753 Namespace::Value,
754 Namespace::Value,
755 Namespace::Label,
756 Namespace::Value,
757 ];
758 p.redefinition = Redefinition::LastWins;
759 p.class_scope = ClassScope::BodyOnly;
760 p.module_scope = ModuleScope::Lexical;
761 p.roots = [RootBinding::Unsupported; 3];
762 p.enforce_visibility = false;
763 p.reexport = Reexport::Always;
764 p.implicit_globals = true;
765 p
766 }
767
768 // ------------------------------------------------------------ setters
769
770 /// Sets when items of `class` become visible.
771 ///
772 /// # Examples
773 ///
774 /// ```
775 /// use resolve_lang::{Hoist, ItemClass, Policy};
776 ///
777 /// let p = Policy::new().with_hoisting(ItemClass::Global, Hoist::AfterDecl);
778 /// assert_eq!(p.hoisting(ItemClass::Global), Hoist::AfterDecl);
779 /// ```
780 #[must_use]
781 pub const fn with_hoisting(mut self, class: ItemClass, hoist: Hoist) -> Self {
782 self.hoist[class.index()] = hoist;
783 self
784 }
785
786 /// Sets the namespaces items of `class` define their name in. Imports
787 /// ignore this (an import binds whatever its target defines).
788 ///
789 /// # Examples
790 ///
791 /// ```
792 /// use resolve_lang::{ItemClass, Namespace, NsSet, Policy};
793 ///
794 /// // Rust: a braced struct defines no constructor value.
795 /// let p = Policy::new().with_occupies(ItemClass::Record, NsSet::single(Namespace::Type));
796 /// assert!(!p.occupies(ItemClass::Record).contains(Namespace::Value));
797 /// ```
798 #[must_use]
799 pub const fn with_occupies(mut self, class: ItemClass, set: NsSet) -> Self {
800 self.occupies[class.index()] = set;
801 self
802 }
803
804 /// Merges namespace `from` into the table of `into`: afterwards both (and
805 /// everything already merged with either) share one table, so a name
806 /// defined in one conflicts with and is found by the other. The merged
807 /// table keeps `into`'s [`Case`].
808 ///
809 /// # Examples
810 ///
811 /// ```
812 /// use resolve_lang::{Namespace, Policy};
813 ///
814 /// let p = Policy::new().with_merge(Namespace::Value, Namespace::Type);
815 /// // Modules already share the type table, so all three are one table now.
816 /// assert_eq!(p.table(Namespace::Value), p.table(Namespace::Module));
817 /// ```
818 #[must_use]
819 pub const fn with_merge(mut self, from: Namespace, into: Namespace) -> Self {
820 let old = self.merge[from.index()];
821 let new = self.merge[into.index()];
822 let mut i = 0;
823 while i < 6 {
824 if self.merge[i] as u8 == old as u8 {
825 self.merge[i] = new;
826 }
827 i += 1;
828 }
829 self
830 }
831
832 /// Sets how the table of `ns` (and every namespace merged with it)
833 /// compares names. Merge first, then set the case: a later merge keeps
834 /// the target table's case.
835 ///
836 /// # Examples
837 ///
838 /// ```
839 /// use resolve_lang::{Case, Namespace, Policy};
840 ///
841 /// let p = Policy::new().with_case(Namespace::Type, Case::AsciiInsensitive);
842 /// // Modules share the type table, so they fold too.
843 /// assert_eq!(p.case(Namespace::Module), Case::AsciiInsensitive);
844 /// assert_eq!(p.case(Namespace::Value), Case::Sensitive);
845 /// ```
846 #[must_use]
847 pub const fn with_case(mut self, ns: Namespace, case: Case) -> Self {
848 self.case[self.merge[ns.index()].index()] = case;
849 self
850 }
851
852 /// Sets the shadowing rule for binders.
853 ///
854 /// # Examples
855 ///
856 /// ```
857 /// use resolve_lang::{Policy, Shadowing};
858 ///
859 /// assert_eq!(Policy::new().with_shadowing(Shadowing::DenyLocals).shadowing(), Shadowing::DenyLocals);
860 /// ```
861 #[must_use]
862 pub const fn with_shadowing(mut self, shadowing: Shadowing) -> Self {
863 self.shadowing = shadowing;
864 self
865 }
866
867 /// Sets what duplicate definitions mean.
868 ///
869 /// # Examples
870 ///
871 /// ```
872 /// use resolve_lang::{Policy, Redefinition};
873 ///
874 /// let p = Policy::new().with_redefinition(Redefinition::LastWins);
875 /// assert_eq!(p.redefinition(), Redefinition::LastWins);
876 /// ```
877 #[must_use]
878 pub const fn with_redefinition(mut self, redefinition: Redefinition) -> Self {
879 self.redefinition = redefinition;
880 self
881 }
882
883 /// Sets what code inside a class sees of its members.
884 ///
885 /// # Examples
886 ///
887 /// ```
888 /// use resolve_lang::{ClassScope, Policy};
889 ///
890 /// let p = Policy::new().with_class_scope(ClassScope::Qualified);
891 /// assert_eq!(p.class_scope(), ClassScope::Qualified);
892 /// ```
893 #[must_use]
894 pub const fn with_class_scope(mut self, class_scope: ClassScope) -> Self {
895 self.class_scope = class_scope;
896 self
897 }
898
899 /// Sets whether nested modules see outer names.
900 ///
901 /// # Examples
902 ///
903 /// ```
904 /// use resolve_lang::{ModuleScope, Policy};
905 ///
906 /// let p = Policy::new().with_module_scope(ModuleScope::Lexical);
907 /// assert_eq!(p.module_scope(), ModuleScope::Lexical);
908 /// ```
909 #[must_use]
910 pub const fn with_module_scope(mut self, module_scope: ModuleScope) -> Self {
911 self.module_scope = module_scope;
912 self
913 }
914
915 /// Sets how `self::`, `parent::`, and `static::` bind.
916 ///
917 /// # Examples
918 ///
919 /// ```
920 /// use resolve_lang::{Policy, RootBinding};
921 ///
922 /// let p = Policy::new().with_roots(RootBinding::Early, RootBinding::Early, RootBinding::Late);
923 /// assert_eq!(p.parent_root(), RootBinding::Early);
924 /// ```
925 #[must_use]
926 pub const fn with_roots(
927 mut self,
928 self_root: RootBinding,
929 parent_root: RootBinding,
930 static_root: RootBinding,
931 ) -> Self {
932 self.roots = [self_root, parent_root, static_root];
933 self
934 }
935
936 /// Sets whether visibility is enforced, and whether private items of a
937 /// module are visible to its descendant modules.
938 ///
939 /// # Examples
940 ///
941 /// ```
942 /// use resolve_lang::Policy;
943 ///
944 /// let p = Policy::new().with_visibility(true, false);
945 /// assert!(p.visibility_enforced());
946 /// assert!(!p.private_to_descendants());
947 /// ```
948 #[must_use]
949 pub const fn with_visibility(mut self, enforce: bool, private_to_descendants: bool) -> Self {
950 self.enforce_visibility = enforce;
951 self.private_to_descendants = private_to_descendants;
952 self
953 }
954
955 /// Sets which import forms the language has, and how imports re-export.
956 ///
957 /// # Examples
958 ///
959 /// ```
960 /// use resolve_lang::{Policy, Reexport};
961 ///
962 /// let p = Policy::new().with_imports(false, true, Reexport::Never);
963 /// assert!(!p.globs_allowed());
964 /// assert!(p.aliases_allowed());
965 /// ```
966 #[must_use]
967 pub const fn with_imports(mut self, globs: bool, aliases: bool, reexport: Reexport) -> Self {
968 self.globs = globs;
969 self.aliases = aliases;
970 self.reexport = reexport;
971 self
972 }
973
974 /// Sets whether a `global` declaration naming no known global binds a host
975 /// global (`Res::Extern`) instead of being reported.
976 ///
977 /// # Examples
978 ///
979 /// ```
980 /// use resolve_lang::Policy;
981 ///
982 /// assert!(Policy::new().with_implicit_globals(true).implicit_globals());
983 /// ```
984 #[must_use]
985 pub const fn with_implicit_globals(mut self, implicit: bool) -> Self {
986 self.implicit_globals = implicit;
987 self
988 }
989
990 /// Sets whether a class may be named in value position.
991 ///
992 /// Languages whose classes are not values still write class names where
993 /// a value goes: `new Foo(1)` lowers to a call whose first operand is
994 /// `Foo`, and `$x instanceof Foo` takes it as its right operand. With
995 /// this set, a value path that finds nothing in the value tables falls
996 /// back to the type table and resolves to a class (or record) item there,
997 /// or to a class the host defines. It applies to unqualified names
998 /// (`Foo`), module-qualified ones (`App\Foo`, `\App\Foo`), and imported
999 /// ones, folded by the type table's case rule. A constant or function of
1000 /// the same name wins, and a path that is the callee of a call does not
1001 /// fall back (`Foo()` is not `new Foo`). `Foo::bar`, `Foo::C`, and
1002 /// `Foo::class` need no fallback: their first segment is already looked
1003 /// up as a container.
1004 ///
1005 /// An interface (or a sum) stays an error in value position: hir-lang
1006 /// refuses a value path that resolves to one, so a lowering names an
1007 /// interface through a type argument.
1008 ///
1009 /// Off by default; [`php`](Self::php) turns it on.
1010 ///
1011 /// # Examples
1012 ///
1013 /// ```
1014 /// use resolve_lang::Policy;
1015 ///
1016 /// assert!(Policy::php().class_values());
1017 /// assert!(!Policy::kraken().class_values());
1018 /// assert!(Policy::kraken().with_class_values(true).class_values());
1019 /// ```
1020 #[must_use]
1021 pub const fn with_class_values(mut self, on: bool) -> Self {
1022 self.class_values = on;
1023 self
1024 }
1025
1026 /// Sets whether a class inherits the members of the interfaces it
1027 /// implements.
1028 ///
1029 /// With this set, `Impl::NAME` finds a constant (or method) declared on
1030 /// an interface `Impl` implements, directly or through a base class or a
1031 /// super-interface, when neither the class nor its base classes declare
1032 /// it; PHP's lookup order. Without it, interfaces contribute no members
1033 /// to the classes implementing them.
1034 ///
1035 /// Off by default; [`php`](Self::php) turns it on.
1036 ///
1037 /// # Examples
1038 ///
1039 /// ```
1040 /// use resolve_lang::Policy;
1041 ///
1042 /// assert!(Policy::php().interface_members());
1043 /// assert!(!Policy::python().interface_members());
1044 /// ```
1045 #[must_use]
1046 pub const fn with_interface_members(mut self, on: bool) -> Self {
1047 self.interface_members = on;
1048 self
1049 }
1050
1051 // ------------------------------------------------------------ getters
1052
1053 /// When items of `class` become visible.
1054 ///
1055 /// # Examples
1056 ///
1057 /// ```
1058 /// use resolve_lang::{Hoist, ItemClass, Policy};
1059 ///
1060 /// assert_eq!(Policy::new().hoisting(ItemClass::Fn), Hoist::Scope);
1061 /// ```
1062 #[must_use]
1063 pub const fn hoisting(&self, class: ItemClass) -> Hoist {
1064 self.hoist[class.index()]
1065 }
1066
1067 /// The namespaces items of `class` define their name in (before merging).
1068 ///
1069 /// # Examples
1070 ///
1071 /// ```
1072 /// use resolve_lang::{ItemClass, Namespace, Policy};
1073 ///
1074 /// assert!(Policy::new().occupies(ItemClass::Fn).contains(Namespace::Value));
1075 /// ```
1076 #[must_use]
1077 pub const fn occupies(&self, class: ItemClass) -> NsSet {
1078 self.occupies[class.index()]
1079 }
1080
1081 /// The table namespace `ns` is stored in after merging.
1082 ///
1083 /// # Examples
1084 ///
1085 /// ```
1086 /// use resolve_lang::{Namespace, Policy};
1087 ///
1088 /// assert_eq!(Policy::new().table(Namespace::Module), Namespace::Type);
1089 /// assert_eq!(Policy::new().table(Namespace::Value), Namespace::Value);
1090 /// ```
1091 #[must_use]
1092 pub const fn table(&self, ns: Namespace) -> Namespace {
1093 self.merge[ns.index()]
1094 }
1095
1096 /// How the table of `ns` compares names.
1097 ///
1098 /// # Examples
1099 ///
1100 /// ```
1101 /// use resolve_lang::{Case, Namespace, Policy};
1102 ///
1103 /// assert_eq!(Policy::new().case(Namespace::Value), Case::Sensitive);
1104 /// ```
1105 #[must_use]
1106 pub const fn case(&self, ns: Namespace) -> Case {
1107 self.case[self.merge[ns.index()].index()]
1108 }
1109
1110 /// The shadowing rule.
1111 ///
1112 /// # Examples
1113 ///
1114 /// ```
1115 /// use resolve_lang::{Policy, Shadowing};
1116 ///
1117 /// assert_eq!(Policy::new().shadowing(), Shadowing::Allow);
1118 /// ```
1119 #[must_use]
1120 pub const fn shadowing(&self) -> Shadowing {
1121 self.shadowing
1122 }
1123
1124 /// What duplicate definitions mean.
1125 ///
1126 /// # Examples
1127 ///
1128 /// ```
1129 /// use resolve_lang::{Policy, Redefinition};
1130 ///
1131 /// assert_eq!(Policy::php().redefinition(), Redefinition::Error);
1132 /// ```
1133 #[must_use]
1134 pub const fn redefinition(&self) -> Redefinition {
1135 self.redefinition
1136 }
1137
1138 /// What code inside a class sees of its members.
1139 ///
1140 /// # Examples
1141 ///
1142 /// ```
1143 /// use resolve_lang::{ClassScope, Policy};
1144 ///
1145 /// assert_eq!(Policy::new().class_scope(), ClassScope::Lexical);
1146 /// ```
1147 #[must_use]
1148 pub const fn class_scope(&self) -> ClassScope {
1149 self.class_scope
1150 }
1151
1152 /// Whether nested modules see outer names.
1153 ///
1154 /// # Examples
1155 ///
1156 /// ```
1157 /// use resolve_lang::{ModuleScope, Policy};
1158 ///
1159 /// assert_eq!(Policy::php().module_scope(), ModuleScope::Lexical);
1160 /// ```
1161 #[must_use]
1162 pub const fn module_scope(&self) -> ModuleScope {
1163 self.module_scope
1164 }
1165
1166 /// How `self::` binds.
1167 ///
1168 /// # Examples
1169 ///
1170 /// ```
1171 /// use resolve_lang::{Policy, RootBinding};
1172 ///
1173 /// assert_eq!(Policy::python().self_root(), RootBinding::Unsupported);
1174 /// ```
1175 #[must_use]
1176 pub const fn self_root(&self) -> RootBinding {
1177 self.roots[0]
1178 }
1179
1180 /// How `parent::` binds.
1181 ///
1182 /// # Examples
1183 ///
1184 /// ```
1185 /// use resolve_lang::{Policy, RootBinding};
1186 ///
1187 /// assert_eq!(Policy::kraken().parent_root(), RootBinding::Unsupported);
1188 /// ```
1189 #[must_use]
1190 pub const fn parent_root(&self) -> RootBinding {
1191 self.roots[1]
1192 }
1193
1194 /// How `static::` binds.
1195 ///
1196 /// # Examples
1197 ///
1198 /// ```
1199 /// use resolve_lang::{Policy, RootBinding};
1200 ///
1201 /// assert_eq!(Policy::php().static_root(), RootBinding::Late);
1202 /// ```
1203 #[must_use]
1204 pub const fn static_root(&self) -> RootBinding {
1205 self.roots[2]
1206 }
1207
1208 /// Whether visibility (`Vis`) is enforced.
1209 ///
1210 /// # Examples
1211 ///
1212 /// ```
1213 /// use resolve_lang::Policy;
1214 ///
1215 /// assert!(Policy::php().visibility_enforced());
1216 /// ```
1217 #[must_use]
1218 pub const fn visibility_enforced(&self) -> bool {
1219 self.enforce_visibility
1220 }
1221
1222 /// Whether private items of a module are visible in its descendant
1223 /// modules (Rust's rule).
1224 ///
1225 /// # Examples
1226 ///
1227 /// ```
1228 /// use resolve_lang::Policy;
1229 ///
1230 /// assert!(Policy::new().private_to_descendants());
1231 /// ```
1232 #[must_use]
1233 pub const fn private_to_descendants(&self) -> bool {
1234 self.private_to_descendants
1235 }
1236
1237 /// Whether glob imports are allowed.
1238 ///
1239 /// # Examples
1240 ///
1241 /// ```
1242 /// use resolve_lang::Policy;
1243 ///
1244 /// assert!(Policy::python().globs_allowed());
1245 /// ```
1246 #[must_use]
1247 pub const fn globs_allowed(&self) -> bool {
1248 self.globs
1249 }
1250
1251 /// Whether aliased imports (`use x as y`) are allowed.
1252 ///
1253 /// # Examples
1254 ///
1255 /// ```
1256 /// use resolve_lang::Policy;
1257 ///
1258 /// assert!(Policy::php().aliases_allowed());
1259 /// ```
1260 #[must_use]
1261 pub const fn aliases_allowed(&self) -> bool {
1262 self.aliases
1263 }
1264
1265 /// How imports re-export.
1266 ///
1267 /// # Examples
1268 ///
1269 /// ```
1270 /// use resolve_lang::{Policy, Reexport};
1271 ///
1272 /// assert_eq!(Policy::php().reexport(), Reexport::Never);
1273 /// ```
1274 #[must_use]
1275 pub const fn reexport(&self) -> Reexport {
1276 self.reexport
1277 }
1278
1279 /// Whether an unknown `global` name binds a host global.
1280 ///
1281 /// # Examples
1282 ///
1283 /// ```
1284 /// use resolve_lang::Policy;
1285 ///
1286 /// assert!(!Policy::kraken().implicit_globals());
1287 /// ```
1288 #[must_use]
1289 pub const fn implicit_globals(&self) -> bool {
1290 self.implicit_globals
1291 }
1292
1293 /// Whether a class-like item may be named in value position
1294 /// ([`with_class_values`](Self::with_class_values)).
1295 ///
1296 /// # Examples
1297 ///
1298 /// ```
1299 /// use resolve_lang::Policy;
1300 ///
1301 /// assert!(Policy::php().class_values());
1302 /// ```
1303 #[must_use]
1304 pub const fn class_values(&self) -> bool {
1305 self.class_values
1306 }
1307
1308 /// Whether classes inherit the members of their interfaces
1309 /// ([`with_interface_members`](Self::with_interface_members)).
1310 ///
1311 /// # Examples
1312 ///
1313 /// ```
1314 /// use resolve_lang::Policy;
1315 ///
1316 /// assert!(Policy::php().interface_members());
1317 /// ```
1318 #[must_use]
1319 pub const fn interface_members(&self) -> bool {
1320 self.interface_members
1321 }
1322
1323 /// The export visibility of an import whose item says `declared`.
1324 pub(crate) const fn import_vis(&self, declared: Vis) -> Vis {
1325 match self.reexport {
1326 Reexport::AsDeclared => declared,
1327 Reexport::Always => Vis::Public,
1328 Reexport::Never => Vis::Private,
1329 }
1330 }
1331
1332 /// The table index (`0..TABLES`) of namespace `ns`.
1333 #[inline]
1334 pub(crate) const fn table_ix(&self, ns: Namespace) -> u8 {
1335 self.merge[ns.index()] as u8
1336 }
1337
1338 /// Whether table `t` (a table index) folds case.
1339 #[inline]
1340 pub(crate) const fn table_folds(&self, t: u8) -> bool {
1341 (t as usize) < TABLES && !matches!(self.case[t as usize], Case::Sensitive)
1342 }
1343
1344 /// Whether any table folds case.
1345 pub(crate) const fn any_folds(&self) -> bool {
1346 let mut t = 0u8;
1347 while (t as usize) < TABLES {
1348 if self.table_folds(t) {
1349 return true;
1350 }
1351 t += 1;
1352 }
1353 false
1354 }
1355}
1356
1357/// The number of name tables (one per [`Namespace`]).
1358pub(crate) const TABLES: usize = 6;
1359
1360/// The rank of a visibility, for "the smaller of two" (glob re-exports).
1361pub(crate) const fn vis_rank(v: Vis) -> u8 {
1362 match v {
1363 Vis::Private => 0,
1364 Vis::Protected => 1,
1365 Vis::Package => 2,
1366 Vis::Public => 3,
1367 }
1368}
1369
1370/// The narrower of two visibilities.
1371pub(crate) const fn min_vis(a: Vis, b: Vis) -> Vis {
1372 if vis_rank(a) <= vis_rank(b) { a } else { b }
1373}
1374
1375#[cfg(test)]
1376mod tests {
1377 use super::*;
1378
1379 #[test]
1380 fn test_merge_is_transitive() {
1381 let p = Policy::new()
1382 .with_merge(Namespace::Macro, Namespace::Value)
1383 .with_merge(Namespace::Value, Namespace::Type);
1384 assert_eq!(p.table(Namespace::Macro), Namespace::Type);
1385 assert_eq!(p.table(Namespace::Value), Namespace::Type);
1386 assert_eq!(p.table(Namespace::Module), Namespace::Type);
1387 assert_eq!(p.table(Namespace::Label), Namespace::Label);
1388 }
1389
1390 #[test]
1391 fn test_min_vis_orders_private_lowest() {
1392 assert_eq!(min_vis(Vis::Public, Vis::Private), Vis::Private);
1393 assert_eq!(min_vis(Vis::Package, Vis::Public), Vis::Package);
1394 assert_eq!(min_vis(Vis::Protected, Vis::Package), Vis::Protected);
1395 }
1396
1397 #[test]
1398 fn test_import_vis_follows_reexport() {
1399 assert_eq!(Policy::new().import_vis(Vis::Public), Vis::Public);
1400 assert_eq!(Policy::new().import_vis(Vis::Private), Vis::Private);
1401 assert_eq!(Policy::python().import_vis(Vis::Private), Vis::Public);
1402 assert_eq!(Policy::php().import_vis(Vis::Public), Vis::Private);
1403 }
1404
1405 #[test]
1406 fn test_class_settings_are_php_only_and_independent() {
1407 for p in [Policy::new(), Policy::kraken(), Policy::python()] {
1408 assert!(!p.class_values() && !p.interface_members());
1409 }
1410 let php = Policy::php();
1411 assert!(php.class_values() && php.interface_members());
1412 let p = php.with_class_values(false);
1413 assert!(!p.class_values() && p.interface_members());
1414 let p = php.with_interface_members(false);
1415 assert!(p.class_values() && !p.interface_members());
1416 // The settings are part of the policy's identity.
1417 assert_ne!(php, php.with_class_values(false));
1418 assert_ne!(Policy::new(), Policy::new().with_interface_members(true));
1419 }
1420
1421 #[test]
1422 fn test_namespace_index_round_trips() {
1423 for ns in Namespace::ALL {
1424 assert_eq!(Namespace::from_index(ns.index()), ns);
1425 }
1426 }
1427}