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).
18///
19/// # Examples
20///
21/// ```
22/// use resolve_lang::Namespace;
23///
24/// assert_eq!(Namespace::Value.name(), "value");
25/// assert_eq!(Namespace::ALL.len(), 5);
26/// ```
27#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub enum Namespace {
29    /// Functions, constants, globals, constructors.
30    Value,
31    /// Records, sums, classes, interfaces, aliases.
32    Type,
33    /// Modules and units.
34    Module,
35    /// Macros. HIR has no macro items (macros expand before lowering); hosts
36    /// may still export names here through [`Env`](crate::Env).
37    Macro,
38    /// Loop and block labels. Labels are binders resolved by lowering, so no
39    /// HIR path is looked up here; the namespace exists for merging rules.
40    Label,
41}
42
43impl Namespace {
44    /// Every namespace, in table order.
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use resolve_lang::Namespace;
50    ///
51    /// assert_eq!(Namespace::ALL[0], Namespace::Value);
52    /// ```
53    pub const ALL: [Self; 5] = [
54        Self::Value,
55        Self::Type,
56        Self::Module,
57        Self::Macro,
58        Self::Label,
59    ];
60
61    /// The lowercase name, for messages.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use resolve_lang::Namespace;
67    ///
68    /// assert_eq!(Namespace::Module.name(), "module");
69    /// ```
70    #[must_use]
71    pub const fn name(self) -> &'static str {
72        match self {
73            Self::Value => "value",
74            Self::Type => "type",
75            Self::Module => "module",
76            Self::Macro => "macro",
77            Self::Label => "label",
78        }
79    }
80
81    #[inline]
82    pub(crate) const fn index(self) -> usize {
83        self as usize
84    }
85
86    pub(crate) const fn from_index(i: usize) -> Self {
87        match i {
88            0 => Self::Value,
89            1 => Self::Type,
90            2 => Self::Module,
91            3 => Self::Macro,
92            _ => Self::Label,
93        }
94    }
95}
96
97/// A set of [`Namespace`]s, as a bit set.
98///
99/// # Examples
100///
101/// ```
102/// use resolve_lang::{Namespace, NsSet};
103///
104/// let set = NsSet::of(&[Namespace::Type, Namespace::Value]);
105/// assert!(set.contains(Namespace::Type));
106/// assert!(!set.contains(Namespace::Module));
107/// assert_eq!(set.iter().count(), 2);
108/// ```
109#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
110pub struct NsSet(u8);
111
112impl NsSet {
113    /// The empty set.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use resolve_lang::NsSet;
119    ///
120    /// assert!(NsSet::EMPTY.is_empty());
121    /// ```
122    pub const EMPTY: Self = Self(0);
123
124    /// A set holding exactly `namespaces`.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use resolve_lang::{Namespace, NsSet};
130    ///
131    /// assert_eq!(NsSet::of(&[Namespace::Value]), NsSet::single(Namespace::Value));
132    /// ```
133    #[must_use]
134    pub const fn of(namespaces: &[Namespace]) -> Self {
135        let mut bits = 0u8;
136        let mut i = 0;
137        while i < namespaces.len() {
138            bits |= 1 << namespaces[i] as u8;
139            i += 1;
140        }
141        Self(bits)
142    }
143
144    /// A set holding one namespace.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use resolve_lang::{Namespace, NsSet};
150    ///
151    /// assert!(NsSet::single(Namespace::Macro).contains(Namespace::Macro));
152    /// ```
153    #[must_use]
154    pub const fn single(ns: Namespace) -> Self {
155        Self(1 << ns as u8)
156    }
157
158    /// Whether `ns` is in the set.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// use resolve_lang::{Namespace, NsSet};
164    ///
165    /// assert!(!NsSet::EMPTY.contains(Namespace::Value));
166    /// ```
167    #[must_use]
168    pub const fn contains(self, ns: Namespace) -> bool {
169        self.0 & (1 << ns as u8) != 0
170    }
171
172    /// Whether the set is empty.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use resolve_lang::{Namespace, NsSet};
178    ///
179    /// assert!(!NsSet::single(Namespace::Type).is_empty());
180    /// ```
181    #[must_use]
182    pub const fn is_empty(self) -> bool {
183        self.0 == 0
184    }
185
186    /// The set with `ns` added.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use resolve_lang::{Namespace, NsSet};
192    ///
193    /// let s = NsSet::EMPTY.with(Namespace::Type);
194    /// assert!(s.contains(Namespace::Type));
195    /// ```
196    #[must_use]
197    pub const fn with(self, ns: Namespace) -> Self {
198        Self(self.0 | (1 << ns as u8))
199    }
200
201    /// The namespaces in the set, in table order.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use resolve_lang::{Namespace, NsSet};
207    ///
208    /// let v: Vec<_> = NsSet::of(&[Namespace::Module, Namespace::Value]).iter().collect();
209    /// assert_eq!(v, [Namespace::Value, Namespace::Module]);
210    /// ```
211    pub fn iter(self) -> impl Iterator<Item = Namespace> {
212        Namespace::ALL
213            .into_iter()
214            .filter(move |ns| self.contains(*ns))
215    }
216}
217
218/// The kinds of named items a policy distinguishes.
219///
220/// # Examples
221///
222/// ```
223/// use resolve_lang::ItemClass;
224///
225/// assert_eq!(ItemClass::ALL.len(), 11);
226/// ```
227#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
228pub enum ItemClass {
229    /// A function or method.
230    Fn,
231    /// A record (struct).
232    Record,
233    /// A sum (enum).
234    Sum,
235    /// A class (or PHP trait, a mixin class).
236    Class,
237    /// An interface or trait.
238    Interface,
239    /// A type alias.
240    Alias,
241    /// An associated type.
242    AssocType,
243    /// A constant.
244    Const,
245    /// A global variable (a static, a Python module-level variable).
246    Global,
247    /// A module.
248    Module,
249    /// An import (`use`, `import`, `from … import`).
250    Import,
251}
252
253impl ItemClass {
254    /// Every item class.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use resolve_lang::ItemClass;
260    ///
261    /// assert_eq!(ItemClass::ALL[0], ItemClass::Fn);
262    /// ```
263    pub const ALL: [Self; 11] = [
264        Self::Fn,
265        Self::Record,
266        Self::Sum,
267        Self::Class,
268        Self::Interface,
269        Self::Alias,
270        Self::AssocType,
271        Self::Const,
272        Self::Global,
273        Self::Module,
274        Self::Import,
275    ];
276
277    /// The class of a HIR item kind, or `None` for kinds that bind no name
278    /// (`impl`, mixin use, error items).
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use hir_lang::{FnDef, ItemKind};
284    /// use resolve_lang::ItemClass;
285    ///
286    /// assert_eq!(ItemClass::of(&ItemKind::Fn(FnDef::default())), Some(ItemClass::Fn));
287    /// assert_eq!(ItemClass::of(&ItemKind::Err), None);
288    /// ```
289    #[must_use]
290    pub const fn of(kind: &ItemKind) -> Option<Self> {
291        Some(match kind {
292            ItemKind::Fn(_) => Self::Fn,
293            ItemKind::Record(_) => Self::Record,
294            ItemKind::Sum(_) => Self::Sum,
295            ItemKind::Class(_) => Self::Class,
296            ItemKind::Interface(_) => Self::Interface,
297            ItemKind::Alias { .. } => Self::Alias,
298            ItemKind::AssocType { .. } => Self::AssocType,
299            ItemKind::Const { .. } => Self::Const,
300            ItemKind::Global { .. } => Self::Global,
301            ItemKind::Module { .. } => Self::Module,
302            ItemKind::Import { .. } => Self::Import,
303            ItemKind::Impl(_) | ItemKind::MixinUse(_) | ItemKind::Err => return None,
304        })
305    }
306
307    #[inline]
308    const fn index(self) -> usize {
309        self as usize
310    }
311}
312
313/// When an item's name becomes visible in its scope.
314///
315/// # Examples
316///
317/// ```
318/// use resolve_lang::{Hoist, ItemClass, Policy};
319///
320/// // PHP: a function declared anywhere is visible in its whole namespace.
321/// assert_eq!(Policy::php().hoisting(ItemClass::Fn), Hoist::Module);
322/// ```
323#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
324pub enum Hoist {
325    /// Visible throughout the scope that declares it, before and after the
326    /// declaration (Rust and Kraken items, Python's per-scope binding rule).
327    Scope,
328    /// Visible from the declaration to the end of its scope, including inside
329    /// itself, so a function can recurse (C-style declare-before-use).
330    AfterDecl,
331    /// Visible throughout the nearest enclosing module, wherever it is
332    /// declared, even inside a function body or a conditional block (PHP
333    /// functions and classes).
334    Module,
335}
336
337/// Whether a binder may reuse a name that is already visible.
338///
339/// # Examples
340///
341/// ```
342/// use resolve_lang::{Policy, Shadowing};
343///
344/// let p = Policy::new().with_shadowing(Shadowing::DenySameScope);
345/// assert_eq!(p.shadowing(), Shadowing::DenySameScope);
346/// ```
347#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
348pub enum Shadowing {
349    /// Any binder may shadow any visible name (Rust `let`, Python, PHP).
350    Allow,
351    /// A binder may not reuse a name bound earlier in the *same* scope
352    /// (JavaScript `let`, a parameter list with a repeated name).
353    DenySameScope,
354    /// A binder may not reuse the name of any local binder visible from the
355    /// same frame (C#, Zig).
356    DenyLocals,
357}
358
359/// What two definitions of one name in one scope and namespace mean.
360///
361/// # Examples
362///
363/// ```
364/// use resolve_lang::{Policy, Redefinition};
365///
366/// assert_eq!(Policy::python().redefinition(), Redefinition::LastWins);
367/// assert_eq!(Policy::kraken().redefinition(), Redefinition::Error);
368/// ```
369#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
370pub enum Redefinition {
371    /// A duplicate definition is an error; the first one is kept.
372    Error,
373    /// A later definition replaces an earlier one (Python `def f` twice).
374    /// With [`Hoist::AfterDecl`] each definition is visible from its own
375    /// declaration on.
376    LastWins,
377}
378
379/// What code nested in a class can see of the class's members unqualified.
380///
381/// # Examples
382///
383/// ```
384/// use resolve_lang::{ClassScope, Policy};
385///
386/// assert_eq!(Policy::python().class_scope(), ClassScope::BodyOnly);
387/// assert_eq!(Policy::php().class_scope(), ClassScope::Qualified);
388/// ```
389#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
390pub enum ClassScope {
391    /// Members are in lexical scope everywhere inside the class, methods
392    /// included (Java, C++, Kraken). Only the class's own members are seen
393    /// unqualified; inherited members go through `self::`/type-directed
394    /// lookup.
395    Lexical,
396    /// Members are visible to the class body's own initializers but not
397    /// inside methods, lambdas, or nested classes (Python).
398    BodyOnly,
399    /// Members are never visible unqualified; code writes `self::x`,
400    /// `static::x`, or `$this->x` (PHP).
401    Qualified,
402}
403
404/// Whether a nested module sees the names of the modules around it.
405///
406/// # Examples
407///
408/// ```
409/// use resolve_lang::{ModuleScope, Policy};
410///
411/// assert_eq!(Policy::kraken().module_scope(), ModuleScope::Isolated);
412/// ```
413#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
414pub enum ModuleScope {
415    /// A nested module starts a fresh lexical world: outer items are reached
416    /// through `super::` or an import (Rust, Kraken).
417    Isolated,
418    /// A nested module sees enclosing scopes' names (PHP namespaces falling
419    /// back outward, languages with lexically nested modules).
420    Lexical,
421}
422
423/// How a type-relative root (`self::`, `parent::`, `static::`) binds.
424///
425/// # Examples
426///
427/// ```
428/// use resolve_lang::{Policy, RootBinding};
429///
430/// // PHP: `static::` is late static binding, resolved at run time.
431/// assert_eq!(Policy::php().static_root(), RootBinding::Late);
432/// assert_eq!(Policy::php().self_root(), RootBinding::Early);
433/// ```
434#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
435pub enum RootBinding {
436    /// Bound at resolution time against the enclosing class (or its first
437    /// base, for `parent::`): a member found there is resolved now; one not
438    /// found is left to type-directed resolution.
439    Early,
440    /// Bound late (at run time or by the type checker): the whole path is
441    /// left type-directed (`unresolved = segments`).
442    Late,
443    /// The language has no such root; a path using it is reported.
444    Unsupported,
445}
446
447/// How an import's own visibility affects re-export.
448///
449/// # Examples
450///
451/// ```
452/// use resolve_lang::{Policy, Reexport};
453///
454/// assert_eq!(Policy::python().reexport(), Reexport::Always);
455/// ```
456#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
457pub enum Reexport {
458    /// An import is visible to other modules exactly as its item's [`Vis`]
459    /// says (`pub use` re-exports, plain `use` does not).
460    AsDeclared,
461    /// Every import becomes part of the module's exports (Python: an imported
462    /// name is a module attribute).
463    Always,
464    /// Imports are never exported (PHP: `use` is file-local).
465    Never,
466}
467
468/// A language's complete scoping policy.
469///
470/// Start from a preset ([`kraken`](Self::kraken), [`php`](Self::php),
471/// [`python`](Self::python)) or [`new`](Self::new) (the lexical default, the
472/// same as Kraken), then adjust with the `with_*` setters. The policy is plain
473/// data, normally read from the language's sketch.
474///
475/// # Examples
476///
477/// ```
478/// use resolve_lang::{ClassScope, Hoist, ItemClass, Namespace, Policy};
479///
480/// let p = Policy::new()
481///     .with_hoisting(ItemClass::Fn, Hoist::AfterDecl)
482///     .with_class_scope(ClassScope::BodyOnly)
483///     .with_merge(Namespace::Type, Namespace::Value);
484/// assert_eq!(p.hoisting(ItemClass::Fn), Hoist::AfterDecl);
485/// assert_eq!(p.table(Namespace::Type), Namespace::Value);
486/// ```
487#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
488pub struct Policy {
489    hoist: [Hoist; 11],
490    occupies: [NsSet; 11],
491    merge: [Namespace; 5],
492    shadowing: Shadowing,
493    redefinition: Redefinition,
494    class_scope: ClassScope,
495    module_scope: ModuleScope,
496    roots: [RootBinding; 3],
497    enforce_visibility: bool,
498    private_to_descendants: bool,
499    globs: bool,
500    aliases: bool,
501    reexport: Reexport,
502    implicit_globals: bool,
503}
504
505impl Default for Policy {
506    fn default() -> Self {
507        Self::new()
508    }
509}
510
511impl Policy {
512    /// The lexical default (equal to [`kraken`](Self::kraken)): every item
513    /// hoisted to its scope; separate value, type, and macro tables with
514    /// modules sharing the type table; class members visible in methods;
515    /// isolated nested modules; `Self::` early, `parent::`/`static::`
516    /// unsupported; visibility enforced with private items visible to child
517    /// modules; globs, aliases, and declared re-exports.
518    ///
519    /// # Examples
520    ///
521    /// ```
522    /// use resolve_lang::{ItemClass, Namespace, NsSet, Policy};
523    ///
524    /// let p = Policy::new();
525    /// assert_eq!(p.occupies(ItemClass::Record), NsSet::of(&[Namespace::Type, Namespace::Value]));
526    /// assert!(p.visibility_enforced());
527    /// ```
528    #[must_use]
529    pub const fn new() -> Self {
530        use Namespace::{Label, Macro, Module, Type, Value};
531        let value = NsSet::single(Value);
532        let ty = NsSet::single(Type);
533        let both = NsSet::of(&[Type, Value]);
534        Self {
535            hoist: [Hoist::Scope; 11],
536            // Fn Record Sum Class Interface Alias AssocType Const Global Module Import
537            occupies: [
538                value,
539                both,
540                ty,
541                both,
542                ty,
543                ty,
544                ty,
545                value,
546                value,
547                NsSet::single(Module),
548                NsSet::EMPTY,
549            ],
550            merge: [Value, Type, Type, Macro, Label],
551            shadowing: Shadowing::Allow,
552            redefinition: Redefinition::Error,
553            class_scope: ClassScope::Lexical,
554            module_scope: ModuleScope::Isolated,
555            roots: [
556                RootBinding::Early,
557                RootBinding::Unsupported,
558                RootBinding::Unsupported,
559            ],
560            enforce_visibility: true,
561            private_to_descendants: true,
562            globs: true,
563            aliases: true,
564            reexport: Reexport::AsDeclared,
565            implicit_globals: false,
566        }
567    }
568
569    /// Kraken and Iron: lexical scoping, modules with imports and
570    /// visibility. Equal to [`new`](Self::new).
571    ///
572    /// # Examples
573    ///
574    /// ```
575    /// use resolve_lang::Policy;
576    ///
577    /// assert_eq!(Policy::kraken(), Policy::new());
578    /// ```
579    #[must_use]
580    pub const fn kraken() -> Self {
581        Self::new()
582    }
583
584    /// PHP (and Mox): functions, classes, interfaces, enums, and constants are
585    /// visible in their whole namespace wherever declared; class members are
586    /// reached only through `self::`/`parent::`/`static::` (late static
587    /// binding for `static::`); `use` imports are file-local and have no
588    /// globs; `global $x` may name a global that does not exist yet.
589    ///
590    /// # Examples
591    ///
592    /// ```
593    /// use resolve_lang::{Hoist, ItemClass, Policy};
594    ///
595    /// let p = Policy::php();
596    /// assert_eq!(p.hoisting(ItemClass::Class), Hoist::Module);
597    /// assert!(!p.globs_allowed());
598    /// assert!(p.implicit_globals());
599    /// ```
600    #[must_use]
601    pub const fn php() -> Self {
602        let mut p = Self::new();
603        let mut i = 0;
604        while i < 11 {
605            let c = ItemClass::ALL[i];
606            if matches!(
607                c,
608                ItemClass::Fn
609                    | ItemClass::Record
610                    | ItemClass::Sum
611                    | ItemClass::Class
612                    | ItemClass::Interface
613                    | ItemClass::Const
614            ) {
615                p.hoist[i] = Hoist::Module;
616            }
617            i += 1;
618        }
619        p.class_scope = ClassScope::Qualified;
620        p.module_scope = ModuleScope::Lexical;
621        p.roots = [RootBinding::Early, RootBinding::Early, RootBinding::Late];
622        p.globs = false;
623        p.reexport = Reexport::Never;
624        p.implicit_globals = true;
625        p
626    }
627
628    /// Python (and Mercury): one table for every kind of name; a name bound
629    /// anywhere in a scope belongs to the whole scope; class bodies are not
630    /// visible to methods; redefinition rebinds; visibility is convention
631    /// only; imports become module attributes.
632    ///
633    /// # Examples
634    ///
635    /// ```
636    /// use resolve_lang::{Namespace, Policy};
637    ///
638    /// let p = Policy::python();
639    /// assert_eq!(p.table(Namespace::Type), Namespace::Value);
640    /// assert_eq!(p.table(Namespace::Module), Namespace::Value);
641    /// assert!(!p.visibility_enforced());
642    /// ```
643    #[must_use]
644    pub const fn python() -> Self {
645        let mut p = Self::new();
646        p.merge = [
647            Namespace::Value,
648            Namespace::Value,
649            Namespace::Value,
650            Namespace::Value,
651            Namespace::Label,
652        ];
653        p.redefinition = Redefinition::LastWins;
654        p.class_scope = ClassScope::BodyOnly;
655        p.module_scope = ModuleScope::Lexical;
656        p.roots = [RootBinding::Unsupported; 3];
657        p.enforce_visibility = false;
658        p.reexport = Reexport::Always;
659        p.implicit_globals = true;
660        p
661    }
662
663    // ------------------------------------------------------------ setters
664
665    /// Sets when items of `class` become visible.
666    ///
667    /// # Examples
668    ///
669    /// ```
670    /// use resolve_lang::{Hoist, ItemClass, Policy};
671    ///
672    /// let p = Policy::new().with_hoisting(ItemClass::Global, Hoist::AfterDecl);
673    /// assert_eq!(p.hoisting(ItemClass::Global), Hoist::AfterDecl);
674    /// ```
675    #[must_use]
676    pub const fn with_hoisting(mut self, class: ItemClass, hoist: Hoist) -> Self {
677        self.hoist[class.index()] = hoist;
678        self
679    }
680
681    /// Sets the namespaces items of `class` define their name in. Imports
682    /// ignore this (an import binds whatever its target defines).
683    ///
684    /// # Examples
685    ///
686    /// ```
687    /// use resolve_lang::{ItemClass, Namespace, NsSet, Policy};
688    ///
689    /// // Rust: a braced struct defines no constructor value.
690    /// let p = Policy::new().with_occupies(ItemClass::Record, NsSet::single(Namespace::Type));
691    /// assert!(!p.occupies(ItemClass::Record).contains(Namespace::Value));
692    /// ```
693    #[must_use]
694    pub const fn with_occupies(mut self, class: ItemClass, set: NsSet) -> Self {
695        self.occupies[class.index()] = set;
696        self
697    }
698
699    /// Merges namespace `from` into the table of `into`: afterwards both (and
700    /// everything already merged with either) share one table, so a name
701    /// defined in one conflicts with and is found by the other.
702    ///
703    /// # Examples
704    ///
705    /// ```
706    /// use resolve_lang::{Namespace, Policy};
707    ///
708    /// let p = Policy::new().with_merge(Namespace::Value, Namespace::Type);
709    /// // Modules already share the type table, so all three are one table now.
710    /// assert_eq!(p.table(Namespace::Value), p.table(Namespace::Module));
711    /// ```
712    #[must_use]
713    pub const fn with_merge(mut self, from: Namespace, into: Namespace) -> Self {
714        let old = self.merge[from.index()];
715        let new = self.merge[into.index()];
716        let mut i = 0;
717        while i < 5 {
718            if self.merge[i] as u8 == old as u8 {
719                self.merge[i] = new;
720            }
721            i += 1;
722        }
723        self
724    }
725
726    /// Sets the shadowing rule for binders.
727    ///
728    /// # Examples
729    ///
730    /// ```
731    /// use resolve_lang::{Policy, Shadowing};
732    ///
733    /// assert_eq!(Policy::new().with_shadowing(Shadowing::DenyLocals).shadowing(), Shadowing::DenyLocals);
734    /// ```
735    #[must_use]
736    pub const fn with_shadowing(mut self, shadowing: Shadowing) -> Self {
737        self.shadowing = shadowing;
738        self
739    }
740
741    /// Sets what duplicate definitions mean.
742    ///
743    /// # Examples
744    ///
745    /// ```
746    /// use resolve_lang::{Policy, Redefinition};
747    ///
748    /// let p = Policy::new().with_redefinition(Redefinition::LastWins);
749    /// assert_eq!(p.redefinition(), Redefinition::LastWins);
750    /// ```
751    #[must_use]
752    pub const fn with_redefinition(mut self, redefinition: Redefinition) -> Self {
753        self.redefinition = redefinition;
754        self
755    }
756
757    /// Sets what code inside a class sees of its members.
758    ///
759    /// # Examples
760    ///
761    /// ```
762    /// use resolve_lang::{ClassScope, Policy};
763    ///
764    /// let p = Policy::new().with_class_scope(ClassScope::Qualified);
765    /// assert_eq!(p.class_scope(), ClassScope::Qualified);
766    /// ```
767    #[must_use]
768    pub const fn with_class_scope(mut self, class_scope: ClassScope) -> Self {
769        self.class_scope = class_scope;
770        self
771    }
772
773    /// Sets whether nested modules see outer names.
774    ///
775    /// # Examples
776    ///
777    /// ```
778    /// use resolve_lang::{ModuleScope, Policy};
779    ///
780    /// let p = Policy::new().with_module_scope(ModuleScope::Lexical);
781    /// assert_eq!(p.module_scope(), ModuleScope::Lexical);
782    /// ```
783    #[must_use]
784    pub const fn with_module_scope(mut self, module_scope: ModuleScope) -> Self {
785        self.module_scope = module_scope;
786        self
787    }
788
789    /// Sets how `self::`, `parent::`, and `static::` bind.
790    ///
791    /// # Examples
792    ///
793    /// ```
794    /// use resolve_lang::{Policy, RootBinding};
795    ///
796    /// let p = Policy::new().with_roots(RootBinding::Early, RootBinding::Early, RootBinding::Late);
797    /// assert_eq!(p.parent_root(), RootBinding::Early);
798    /// ```
799    #[must_use]
800    pub const fn with_roots(
801        mut self,
802        self_root: RootBinding,
803        parent_root: RootBinding,
804        static_root: RootBinding,
805    ) -> Self {
806        self.roots = [self_root, parent_root, static_root];
807        self
808    }
809
810    /// Sets whether visibility is enforced, and whether private items of a
811    /// module are visible to its descendant modules.
812    ///
813    /// # Examples
814    ///
815    /// ```
816    /// use resolve_lang::Policy;
817    ///
818    /// let p = Policy::new().with_visibility(true, false);
819    /// assert!(p.visibility_enforced());
820    /// assert!(!p.private_to_descendants());
821    /// ```
822    #[must_use]
823    pub const fn with_visibility(mut self, enforce: bool, private_to_descendants: bool) -> Self {
824        self.enforce_visibility = enforce;
825        self.private_to_descendants = private_to_descendants;
826        self
827    }
828
829    /// Sets which import forms the language has, and how imports re-export.
830    ///
831    /// # Examples
832    ///
833    /// ```
834    /// use resolve_lang::{Policy, Reexport};
835    ///
836    /// let p = Policy::new().with_imports(false, true, Reexport::Never);
837    /// assert!(!p.globs_allowed());
838    /// assert!(p.aliases_allowed());
839    /// ```
840    #[must_use]
841    pub const fn with_imports(mut self, globs: bool, aliases: bool, reexport: Reexport) -> Self {
842        self.globs = globs;
843        self.aliases = aliases;
844        self.reexport = reexport;
845        self
846    }
847
848    /// Sets whether a `global` declaration naming no known global binds a host
849    /// global (`Res::Extern`) instead of being reported.
850    ///
851    /// # Examples
852    ///
853    /// ```
854    /// use resolve_lang::Policy;
855    ///
856    /// assert!(Policy::new().with_implicit_globals(true).implicit_globals());
857    /// ```
858    #[must_use]
859    pub const fn with_implicit_globals(mut self, implicit: bool) -> Self {
860        self.implicit_globals = implicit;
861        self
862    }
863
864    // ------------------------------------------------------------ getters
865
866    /// When items of `class` become visible.
867    ///
868    /// # Examples
869    ///
870    /// ```
871    /// use resolve_lang::{Hoist, ItemClass, Policy};
872    ///
873    /// assert_eq!(Policy::new().hoisting(ItemClass::Fn), Hoist::Scope);
874    /// ```
875    #[must_use]
876    pub const fn hoisting(&self, class: ItemClass) -> Hoist {
877        self.hoist[class.index()]
878    }
879
880    /// The namespaces items of `class` define their name in (before merging).
881    ///
882    /// # Examples
883    ///
884    /// ```
885    /// use resolve_lang::{ItemClass, Namespace, Policy};
886    ///
887    /// assert!(Policy::new().occupies(ItemClass::Fn).contains(Namespace::Value));
888    /// ```
889    #[must_use]
890    pub const fn occupies(&self, class: ItemClass) -> NsSet {
891        self.occupies[class.index()]
892    }
893
894    /// The table namespace `ns` is stored in after merging.
895    ///
896    /// # Examples
897    ///
898    /// ```
899    /// use resolve_lang::{Namespace, Policy};
900    ///
901    /// assert_eq!(Policy::new().table(Namespace::Module), Namespace::Type);
902    /// assert_eq!(Policy::new().table(Namespace::Value), Namespace::Value);
903    /// ```
904    #[must_use]
905    pub const fn table(&self, ns: Namespace) -> Namespace {
906        self.merge[ns.index()]
907    }
908
909    /// The shadowing rule.
910    ///
911    /// # Examples
912    ///
913    /// ```
914    /// use resolve_lang::{Policy, Shadowing};
915    ///
916    /// assert_eq!(Policy::new().shadowing(), Shadowing::Allow);
917    /// ```
918    #[must_use]
919    pub const fn shadowing(&self) -> Shadowing {
920        self.shadowing
921    }
922
923    /// What duplicate definitions mean.
924    ///
925    /// # Examples
926    ///
927    /// ```
928    /// use resolve_lang::{Policy, Redefinition};
929    ///
930    /// assert_eq!(Policy::php().redefinition(), Redefinition::Error);
931    /// ```
932    #[must_use]
933    pub const fn redefinition(&self) -> Redefinition {
934        self.redefinition
935    }
936
937    /// What code inside a class sees of its members.
938    ///
939    /// # Examples
940    ///
941    /// ```
942    /// use resolve_lang::{ClassScope, Policy};
943    ///
944    /// assert_eq!(Policy::new().class_scope(), ClassScope::Lexical);
945    /// ```
946    #[must_use]
947    pub const fn class_scope(&self) -> ClassScope {
948        self.class_scope
949    }
950
951    /// Whether nested modules see outer names.
952    ///
953    /// # Examples
954    ///
955    /// ```
956    /// use resolve_lang::{ModuleScope, Policy};
957    ///
958    /// assert_eq!(Policy::php().module_scope(), ModuleScope::Lexical);
959    /// ```
960    #[must_use]
961    pub const fn module_scope(&self) -> ModuleScope {
962        self.module_scope
963    }
964
965    /// How `self::` binds.
966    ///
967    /// # Examples
968    ///
969    /// ```
970    /// use resolve_lang::{Policy, RootBinding};
971    ///
972    /// assert_eq!(Policy::python().self_root(), RootBinding::Unsupported);
973    /// ```
974    #[must_use]
975    pub const fn self_root(&self) -> RootBinding {
976        self.roots[0]
977    }
978
979    /// How `parent::` binds.
980    ///
981    /// # Examples
982    ///
983    /// ```
984    /// use resolve_lang::{Policy, RootBinding};
985    ///
986    /// assert_eq!(Policy::kraken().parent_root(), RootBinding::Unsupported);
987    /// ```
988    #[must_use]
989    pub const fn parent_root(&self) -> RootBinding {
990        self.roots[1]
991    }
992
993    /// How `static::` binds.
994    ///
995    /// # Examples
996    ///
997    /// ```
998    /// use resolve_lang::{Policy, RootBinding};
999    ///
1000    /// assert_eq!(Policy::php().static_root(), RootBinding::Late);
1001    /// ```
1002    #[must_use]
1003    pub const fn static_root(&self) -> RootBinding {
1004        self.roots[2]
1005    }
1006
1007    /// Whether visibility (`Vis`) is enforced.
1008    ///
1009    /// # Examples
1010    ///
1011    /// ```
1012    /// use resolve_lang::Policy;
1013    ///
1014    /// assert!(Policy::php().visibility_enforced());
1015    /// ```
1016    #[must_use]
1017    pub const fn visibility_enforced(&self) -> bool {
1018        self.enforce_visibility
1019    }
1020
1021    /// Whether private items of a module are visible in its descendant
1022    /// modules (Rust's rule).
1023    ///
1024    /// # Examples
1025    ///
1026    /// ```
1027    /// use resolve_lang::Policy;
1028    ///
1029    /// assert!(Policy::new().private_to_descendants());
1030    /// ```
1031    #[must_use]
1032    pub const fn private_to_descendants(&self) -> bool {
1033        self.private_to_descendants
1034    }
1035
1036    /// Whether glob imports are allowed.
1037    ///
1038    /// # Examples
1039    ///
1040    /// ```
1041    /// use resolve_lang::Policy;
1042    ///
1043    /// assert!(Policy::python().globs_allowed());
1044    /// ```
1045    #[must_use]
1046    pub const fn globs_allowed(&self) -> bool {
1047        self.globs
1048    }
1049
1050    /// Whether aliased imports (`use x as y`) are allowed.
1051    ///
1052    /// # Examples
1053    ///
1054    /// ```
1055    /// use resolve_lang::Policy;
1056    ///
1057    /// assert!(Policy::php().aliases_allowed());
1058    /// ```
1059    #[must_use]
1060    pub const fn aliases_allowed(&self) -> bool {
1061        self.aliases
1062    }
1063
1064    /// How imports re-export.
1065    ///
1066    /// # Examples
1067    ///
1068    /// ```
1069    /// use resolve_lang::{Policy, Reexport};
1070    ///
1071    /// assert_eq!(Policy::php().reexport(), Reexport::Never);
1072    /// ```
1073    #[must_use]
1074    pub const fn reexport(&self) -> Reexport {
1075        self.reexport
1076    }
1077
1078    /// Whether an unknown `global` name binds a host global.
1079    ///
1080    /// # Examples
1081    ///
1082    /// ```
1083    /// use resolve_lang::Policy;
1084    ///
1085    /// assert!(!Policy::kraken().implicit_globals());
1086    /// ```
1087    #[must_use]
1088    pub const fn implicit_globals(&self) -> bool {
1089        self.implicit_globals
1090    }
1091
1092    /// The export visibility of an import whose item says `declared`.
1093    pub(crate) const fn import_vis(&self, declared: Vis) -> Vis {
1094        match self.reexport {
1095            Reexport::AsDeclared => declared,
1096            Reexport::Always => Vis::Public,
1097            Reexport::Never => Vis::Private,
1098        }
1099    }
1100
1101    /// The table index (`0..5`) of namespace `ns`.
1102    #[inline]
1103    pub(crate) const fn table_ix(&self, ns: Namespace) -> u8 {
1104        self.merge[ns.index()] as u8
1105    }
1106}
1107
1108/// The rank of a visibility, for "the smaller of two" (glob re-exports).
1109pub(crate) const fn vis_rank(v: Vis) -> u8 {
1110    match v {
1111        Vis::Private => 0,
1112        Vis::Protected => 1,
1113        Vis::Package => 2,
1114        Vis::Public => 3,
1115    }
1116}
1117
1118/// The narrower of two visibilities.
1119pub(crate) const fn min_vis(a: Vis, b: Vis) -> Vis {
1120    if vis_rank(a) <= vis_rank(b) { a } else { b }
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125    use super::*;
1126
1127    #[test]
1128    fn test_merge_is_transitive() {
1129        let p = Policy::new()
1130            .with_merge(Namespace::Macro, Namespace::Value)
1131            .with_merge(Namespace::Value, Namespace::Type);
1132        assert_eq!(p.table(Namespace::Macro), Namespace::Type);
1133        assert_eq!(p.table(Namespace::Value), Namespace::Type);
1134        assert_eq!(p.table(Namespace::Module), Namespace::Type);
1135        assert_eq!(p.table(Namespace::Label), Namespace::Label);
1136    }
1137
1138    #[test]
1139    fn test_min_vis_orders_private_lowest() {
1140        assert_eq!(min_vis(Vis::Public, Vis::Private), Vis::Private);
1141        assert_eq!(min_vis(Vis::Package, Vis::Public), Vis::Package);
1142        assert_eq!(min_vis(Vis::Protected, Vis::Package), Vis::Protected);
1143    }
1144
1145    #[test]
1146    fn test_import_vis_follows_reexport() {
1147        assert_eq!(Policy::new().import_vis(Vis::Public), Vis::Public);
1148        assert_eq!(Policy::new().import_vis(Vis::Private), Vis::Private);
1149        assert_eq!(Policy::python().import_vis(Vis::Private), Vis::Public);
1150        assert_eq!(Policy::php().import_vis(Vis::Public), Vis::Private);
1151    }
1152
1153    #[test]
1154    fn test_namespace_index_round_trips() {
1155        for ns in Namespace::ALL {
1156            assert_eq!(Namespace::from_index(ns.index()), ns);
1157        }
1158    }
1159}