Skip to main content

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}