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/// # Examples
352///
353/// ```
354/// use resolve_lang::{Policy, Shadowing};
355///
356/// let p = Policy::new().with_shadowing(Shadowing::DenySameScope);
357/// assert_eq!(p.shadowing(), Shadowing::DenySameScope);
358/// ```
359#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
360pub enum Shadowing {
361 /// Any binder may shadow any visible name (Rust `let`, Python, PHP).
362 Allow,
363 /// A binder may not reuse a name bound earlier in the *same* scope
364 /// (JavaScript `let`, a parameter list with a repeated name).
365 DenySameScope,
366 /// A binder may not reuse the name of any local binder visible from the
367 /// same frame (C#, Zig).
368 DenyLocals,
369}
370
371/// What two definitions of one name in one scope and namespace mean.
372///
373/// # Examples
374///
375/// ```
376/// use resolve_lang::{Policy, Redefinition};
377///
378/// assert_eq!(Policy::python().redefinition(), Redefinition::LastWins);
379/// assert_eq!(Policy::kraken().redefinition(), Redefinition::Error);
380/// ```
381#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
382pub enum Redefinition {
383 /// A duplicate definition is an error; the first one is kept.
384 Error,
385 /// A later definition replaces an earlier one (Python `def f` twice).
386 /// With [`Hoist::AfterDecl`] each definition is visible from its own
387 /// declaration on.
388 LastWins,
389}
390
391/// What code nested in a class can see of the class's members unqualified.
392///
393/// # Examples
394///
395/// ```
396/// use resolve_lang::{ClassScope, Policy};
397///
398/// assert_eq!(Policy::python().class_scope(), ClassScope::BodyOnly);
399/// assert_eq!(Policy::php().class_scope(), ClassScope::Qualified);
400/// ```
401#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
402pub enum ClassScope {
403 /// Members are in lexical scope everywhere inside the class, methods
404 /// included (Java, C++, Kraken). Only the class's own members are seen
405 /// unqualified; inherited members go through `self::`/type-directed
406 /// lookup.
407 Lexical,
408 /// Members are visible to the class body's own initializers but not
409 /// inside methods, lambdas, or nested classes (Python).
410 BodyOnly,
411 /// Members are never visible unqualified; code writes `self::x`,
412 /// `static::x`, or `$this->x` (PHP).
413 Qualified,
414}
415
416/// Whether a nested module sees the names of the modules around it.
417///
418/// # Examples
419///
420/// ```
421/// use resolve_lang::{ModuleScope, Policy};
422///
423/// assert_eq!(Policy::kraken().module_scope(), ModuleScope::Isolated);
424/// ```
425#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
426pub enum ModuleScope {
427 /// A nested module starts a fresh lexical world: outer items are reached
428 /// through `super::` or an import (Rust, Kraken).
429 Isolated,
430 /// A nested module sees enclosing scopes' names (PHP namespaces falling
431 /// back outward, languages with lexically nested modules).
432 Lexical,
433}
434
435/// How a type-relative root (`self::`, `parent::`, `static::`) binds.
436///
437/// # Examples
438///
439/// ```
440/// use resolve_lang::{Policy, RootBinding};
441///
442/// // PHP: `static::` is late static binding, resolved at run time.
443/// assert_eq!(Policy::php().static_root(), RootBinding::Late);
444/// assert_eq!(Policy::php().self_root(), RootBinding::Early);
445/// ```
446#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
447pub enum RootBinding {
448 /// Bound at resolution time against the enclosing class (or its first
449 /// base, for `parent::`): a member found there is resolved now; one not
450 /// found is left to type-directed resolution.
451 Early,
452 /// Bound late (at run time or by the type checker): the whole path is
453 /// left type-directed (`unresolved = segments`).
454 Late,
455 /// The language has no such root; a path using it is reported.
456 Unsupported,
457}
458
459/// How an import's own visibility affects re-export.
460///
461/// # Examples
462///
463/// ```
464/// use resolve_lang::{Policy, Reexport};
465///
466/// assert_eq!(Policy::python().reexport(), Reexport::Always);
467/// ```
468#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
469pub enum Reexport {
470 /// An import is visible to other modules exactly as its item's [`Vis`]
471 /// says (`pub use` re-exports, plain `use` does not).
472 AsDeclared,
473 /// Every import becomes part of the module's exports (Python: an imported
474 /// name is a module attribute).
475 Always,
476 /// Imports are never exported (PHP: `use` is file-local).
477 Never,
478}
479
480/// How a namespace's table compares names.
481///
482/// Folding is a property of the *table*: every name stored in it, and every
483/// name looked up in it, is compared through the same fold. Folding is
484/// deterministic and independent of locale; diagnostics, the index, and
485/// [`ClassMember`](crate::ClassMember) keep each name as it was written.
486///
487/// Only ASCII folding is offered: it is what PHP 8 does for function,
488/// class, interface, trait, enum, and namespace names (bytes `A`-`Z` match
489/// `a`-`z`; every other byte, including UTF-8 sequences, must match
490/// exactly). Unicode case folding is not provided: no target language needs
491/// it, and its tables change with the Unicode version, which would make
492/// resolution depend on the toolchain.
493///
494/// # Examples
495///
496/// ```
497/// use resolve_lang::{Case, Namespace, Policy};
498///
499/// assert_eq!(Policy::php().case(Namespace::Value), Case::AsciiInsensitive);
500/// assert_eq!(Policy::php().case(Namespace::Const), Case::Sensitive);
501/// assert_eq!(Policy::python().case(Namespace::Value), Case::Sensitive);
502/// ```
503#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
504#[non_exhaustive]
505pub enum Case {
506 /// Names match only when spelled identically (the default everywhere).
507 Sensitive,
508 /// Names match when they are equal after mapping ASCII `A`-`Z` to
509 /// `a`-`z` (PHP 8).
510 AsciiInsensitive,
511}
512
513/// A language's complete scoping policy.
514///
515/// Start from a preset ([`kraken`](Self::kraken), [`php`](Self::php),
516/// [`python`](Self::python)) or [`new`](Self::new) (the lexical default, the
517/// same as Kraken), then adjust with the `with_*` setters. The policy is plain
518/// data, normally read from the language's sketch.
519///
520/// # Examples
521///
522/// ```
523/// use resolve_lang::{ClassScope, Hoist, ItemClass, Namespace, Policy};
524///
525/// let p = Policy::new()
526/// .with_hoisting(ItemClass::Fn, Hoist::AfterDecl)
527/// .with_class_scope(ClassScope::BodyOnly)
528/// .with_merge(Namespace::Type, Namespace::Value);
529/// assert_eq!(p.hoisting(ItemClass::Fn), Hoist::AfterDecl);
530/// assert_eq!(p.table(Namespace::Type), Namespace::Value);
531/// ```
532#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
533pub struct Policy {
534 hoist: [Hoist; 11],
535 occupies: [NsSet; 11],
536 merge: [Namespace; 6],
537 /// By table index (the namespace a table is merged into).
538 case: [Case; 6],
539 shadowing: Shadowing,
540 redefinition: Redefinition,
541 class_scope: ClassScope,
542 module_scope: ModuleScope,
543 roots: [RootBinding; 3],
544 enforce_visibility: bool,
545 private_to_descendants: bool,
546 globs: bool,
547 aliases: bool,
548 reexport: Reexport,
549 implicit_globals: bool,
550}
551
552impl Default for Policy {
553 fn default() -> Self {
554 Self::new()
555 }
556}
557
558impl Policy {
559 /// The lexical default (equal to [`kraken`](Self::kraken)): every item
560 /// hoisted to its scope; separate value, type, and macro tables with
561 /// modules sharing the type table and constants sharing the value table;
562 /// every table case-sensitive; class members (own and inherited) visible
563 /// in methods;
564 /// isolated nested modules; `Self::` early, `parent::`/`static::`
565 /// unsupported; visibility enforced with private items visible to child
566 /// modules; globs, aliases, and declared re-exports.
567 ///
568 /// # Examples
569 ///
570 /// ```
571 /// use resolve_lang::{ItemClass, Namespace, NsSet, Policy};
572 ///
573 /// let p = Policy::new();
574 /// assert_eq!(p.occupies(ItemClass::Record), NsSet::of(&[Namespace::Type, Namespace::Value]));
575 /// assert!(p.visibility_enforced());
576 /// ```
577 #[must_use]
578 pub const fn new() -> Self {
579 use Namespace::{Const, Label, Macro, Module, Type, Value};
580 let value = NsSet::single(Value);
581 let ty = NsSet::single(Type);
582 let both = NsSet::of(&[Type, Value]);
583 Self {
584 hoist: [Hoist::Scope; 11],
585 // Fn Record Sum Class Interface Alias AssocType Const Global Module Import
586 occupies: [
587 value,
588 both,
589 ty,
590 both,
591 ty,
592 ty,
593 ty,
594 NsSet::single(Const),
595 value,
596 NsSet::single(Module),
597 NsSet::EMPTY,
598 ],
599 merge: [Value, Type, Type, Macro, Label, Value],
600 case: [Case::Sensitive; 6],
601 shadowing: Shadowing::Allow,
602 redefinition: Redefinition::Error,
603 class_scope: ClassScope::Lexical,
604 module_scope: ModuleScope::Isolated,
605 roots: [
606 RootBinding::Early,
607 RootBinding::Unsupported,
608 RootBinding::Unsupported,
609 ],
610 enforce_visibility: true,
611 private_to_descendants: true,
612 globs: true,
613 aliases: true,
614 reexport: Reexport::AsDeclared,
615 implicit_globals: false,
616 }
617 }
618
619 /// Kraken and Iron: lexical scoping, modules with imports and
620 /// visibility. Equal to [`new`](Self::new).
621 ///
622 /// # Examples
623 ///
624 /// ```
625 /// use resolve_lang::Policy;
626 ///
627 /// assert_eq!(Policy::kraken(), Policy::new());
628 /// ```
629 #[must_use]
630 pub const fn kraken() -> Self {
631 Self::new()
632 }
633
634 /// PHP (and Mox): functions, classes, interfaces, enums, and constants are
635 /// visible in their whole namespace wherever declared; class members are
636 /// reached only through `self::`/`parent::`/`static::` (late static
637 /// binding for `static::`); `use` imports are file-local and have no
638 /// globs; `global $x` may name a global that does not exist yet.
639 ///
640 /// Names follow PHP 8's tables and case rules:
641 ///
642 /// | Table | Holds | Case |
643 /// |---|---|---|
644 /// | [`Value`](Namespace::Value) | functions, methods | ASCII-insensitive |
645 /// | [`Type`](Namespace::Type) (with [`Module`](Namespace::Module)) | classes, interfaces, traits, enums, namespaces | ASCII-insensitive |
646 /// | [`Const`](Namespace::Const) | constants, class constants, globals, static properties | sensitive |
647 ///
648 /// So a function and a constant, or a method and a class constant, may
649 /// share a name; `strlen` and `STRLEN` are one function; and a class is
650 /// a type only (construct it through a type path, as `new C` lowers).
651 /// HIR paths carry no `$` sigil, so a sketch that strips it from static
652 /// property names makes a static property and a class constant of the
653 /// same name collide; keep the sigil in property names (`$count`) to
654 /// keep them apart.
655 ///
656 /// # Examples
657 ///
658 /// ```
659 /// use resolve_lang::{Case, Hoist, ItemClass, Namespace, NsSet, Policy};
660 ///
661 /// let p = Policy::php();
662 /// assert_eq!(p.hoisting(ItemClass::Class), Hoist::Module);
663 /// assert_eq!(p.occupies(ItemClass::Class), NsSet::single(Namespace::Type));
664 /// assert_ne!(p.table(Namespace::Const), p.table(Namespace::Value));
665 /// assert_eq!(p.case(Namespace::Type), Case::AsciiInsensitive);
666 /// assert!(!p.globs_allowed());
667 /// assert!(p.implicit_globals());
668 /// ```
669 #[must_use]
670 pub const fn php() -> Self {
671 let mut p = Self::new();
672 let mut i = 0;
673 while i < 11 {
674 let c = ItemClass::ALL[i];
675 if matches!(
676 c,
677 ItemClass::Fn
678 | ItemClass::Record
679 | ItemClass::Sum
680 | ItemClass::Class
681 | ItemClass::Interface
682 | ItemClass::Const
683 ) {
684 p.hoist[i] = Hoist::Module;
685 }
686 if matches!(
687 c,
688 ItemClass::Record | ItemClass::Sum | ItemClass::Class | ItemClass::Interface
689 ) {
690 p.occupies[i] = NsSet::single(Namespace::Type);
691 }
692 i += 1;
693 }
694 p.occupies[ItemClass::Global.index()] = NsSet::single(Namespace::Const);
695 p.merge = [
696 Namespace::Value,
697 Namespace::Type,
698 Namespace::Type,
699 Namespace::Macro,
700 Namespace::Label,
701 Namespace::Const,
702 ];
703 p.case[Namespace::Value.index()] = Case::AsciiInsensitive;
704 p.case[Namespace::Type.index()] = Case::AsciiInsensitive;
705 p.class_scope = ClassScope::Qualified;
706 p.module_scope = ModuleScope::Lexical;
707 p.roots = [RootBinding::Early, RootBinding::Early, RootBinding::Late];
708 p.globs = false;
709 p.reexport = Reexport::Never;
710 p.implicit_globals = true;
711 p
712 }
713
714 /// Python (and Mercury): one case-sensitive table for every kind of
715 /// name; a name bound anywhere in a scope belongs to the whole scope;
716 /// class bodies are not visible to methods; members are inherited in C3
717 /// method resolution order (as under every policy; an inconsistent
718 /// hierarchy is reported, as Python raises `TypeError` at class
719 /// creation); redefinition rebinds; visibility is convention only;
720 /// imports become module attributes.
721 ///
722 /// # Examples
723 ///
724 /// ```
725 /// use resolve_lang::{Namespace, Policy};
726 ///
727 /// let p = Policy::python();
728 /// assert_eq!(p.table(Namespace::Type), Namespace::Value);
729 /// assert_eq!(p.table(Namespace::Module), Namespace::Value);
730 /// assert!(!p.visibility_enforced());
731 /// ```
732 #[must_use]
733 pub const fn python() -> Self {
734 let mut p = Self::new();
735 p.merge = [
736 Namespace::Value,
737 Namespace::Value,
738 Namespace::Value,
739 Namespace::Value,
740 Namespace::Label,
741 Namespace::Value,
742 ];
743 p.redefinition = Redefinition::LastWins;
744 p.class_scope = ClassScope::BodyOnly;
745 p.module_scope = ModuleScope::Lexical;
746 p.roots = [RootBinding::Unsupported; 3];
747 p.enforce_visibility = false;
748 p.reexport = Reexport::Always;
749 p.implicit_globals = true;
750 p
751 }
752
753 // ------------------------------------------------------------ setters
754
755 /// Sets when items of `class` become visible.
756 ///
757 /// # Examples
758 ///
759 /// ```
760 /// use resolve_lang::{Hoist, ItemClass, Policy};
761 ///
762 /// let p = Policy::new().with_hoisting(ItemClass::Global, Hoist::AfterDecl);
763 /// assert_eq!(p.hoisting(ItemClass::Global), Hoist::AfterDecl);
764 /// ```
765 #[must_use]
766 pub const fn with_hoisting(mut self, class: ItemClass, hoist: Hoist) -> Self {
767 self.hoist[class.index()] = hoist;
768 self
769 }
770
771 /// Sets the namespaces items of `class` define their name in. Imports
772 /// ignore this (an import binds whatever its target defines).
773 ///
774 /// # Examples
775 ///
776 /// ```
777 /// use resolve_lang::{ItemClass, Namespace, NsSet, Policy};
778 ///
779 /// // Rust: a braced struct defines no constructor value.
780 /// let p = Policy::new().with_occupies(ItemClass::Record, NsSet::single(Namespace::Type));
781 /// assert!(!p.occupies(ItemClass::Record).contains(Namespace::Value));
782 /// ```
783 #[must_use]
784 pub const fn with_occupies(mut self, class: ItemClass, set: NsSet) -> Self {
785 self.occupies[class.index()] = set;
786 self
787 }
788
789 /// Merges namespace `from` into the table of `into`: afterwards both (and
790 /// everything already merged with either) share one table, so a name
791 /// defined in one conflicts with and is found by the other. The merged
792 /// table keeps `into`'s [`Case`].
793 ///
794 /// # Examples
795 ///
796 /// ```
797 /// use resolve_lang::{Namespace, Policy};
798 ///
799 /// let p = Policy::new().with_merge(Namespace::Value, Namespace::Type);
800 /// // Modules already share the type table, so all three are one table now.
801 /// assert_eq!(p.table(Namespace::Value), p.table(Namespace::Module));
802 /// ```
803 #[must_use]
804 pub const fn with_merge(mut self, from: Namespace, into: Namespace) -> Self {
805 let old = self.merge[from.index()];
806 let new = self.merge[into.index()];
807 let mut i = 0;
808 while i < 6 {
809 if self.merge[i] as u8 == old as u8 {
810 self.merge[i] = new;
811 }
812 i += 1;
813 }
814 self
815 }
816
817 /// Sets how the table of `ns` (and every namespace merged with it)
818 /// compares names. Merge first, then set the case: a later merge keeps
819 /// the target table's case.
820 ///
821 /// # Examples
822 ///
823 /// ```
824 /// use resolve_lang::{Case, Namespace, Policy};
825 ///
826 /// let p = Policy::new().with_case(Namespace::Type, Case::AsciiInsensitive);
827 /// // Modules share the type table, so they fold too.
828 /// assert_eq!(p.case(Namespace::Module), Case::AsciiInsensitive);
829 /// assert_eq!(p.case(Namespace::Value), Case::Sensitive);
830 /// ```
831 #[must_use]
832 pub const fn with_case(mut self, ns: Namespace, case: Case) -> Self {
833 self.case[self.merge[ns.index()].index()] = case;
834 self
835 }
836
837 /// Sets the shadowing rule for binders.
838 ///
839 /// # Examples
840 ///
841 /// ```
842 /// use resolve_lang::{Policy, Shadowing};
843 ///
844 /// assert_eq!(Policy::new().with_shadowing(Shadowing::DenyLocals).shadowing(), Shadowing::DenyLocals);
845 /// ```
846 #[must_use]
847 pub const fn with_shadowing(mut self, shadowing: Shadowing) -> Self {
848 self.shadowing = shadowing;
849 self
850 }
851
852 /// Sets what duplicate definitions mean.
853 ///
854 /// # Examples
855 ///
856 /// ```
857 /// use resolve_lang::{Policy, Redefinition};
858 ///
859 /// let p = Policy::new().with_redefinition(Redefinition::LastWins);
860 /// assert_eq!(p.redefinition(), Redefinition::LastWins);
861 /// ```
862 #[must_use]
863 pub const fn with_redefinition(mut self, redefinition: Redefinition) -> Self {
864 self.redefinition = redefinition;
865 self
866 }
867
868 /// Sets what code inside a class sees of its members.
869 ///
870 /// # Examples
871 ///
872 /// ```
873 /// use resolve_lang::{ClassScope, Policy};
874 ///
875 /// let p = Policy::new().with_class_scope(ClassScope::Qualified);
876 /// assert_eq!(p.class_scope(), ClassScope::Qualified);
877 /// ```
878 #[must_use]
879 pub const fn with_class_scope(mut self, class_scope: ClassScope) -> Self {
880 self.class_scope = class_scope;
881 self
882 }
883
884 /// Sets whether nested modules see outer names.
885 ///
886 /// # Examples
887 ///
888 /// ```
889 /// use resolve_lang::{ModuleScope, Policy};
890 ///
891 /// let p = Policy::new().with_module_scope(ModuleScope::Lexical);
892 /// assert_eq!(p.module_scope(), ModuleScope::Lexical);
893 /// ```
894 #[must_use]
895 pub const fn with_module_scope(mut self, module_scope: ModuleScope) -> Self {
896 self.module_scope = module_scope;
897 self
898 }
899
900 /// Sets how `self::`, `parent::`, and `static::` bind.
901 ///
902 /// # Examples
903 ///
904 /// ```
905 /// use resolve_lang::{Policy, RootBinding};
906 ///
907 /// let p = Policy::new().with_roots(RootBinding::Early, RootBinding::Early, RootBinding::Late);
908 /// assert_eq!(p.parent_root(), RootBinding::Early);
909 /// ```
910 #[must_use]
911 pub const fn with_roots(
912 mut self,
913 self_root: RootBinding,
914 parent_root: RootBinding,
915 static_root: RootBinding,
916 ) -> Self {
917 self.roots = [self_root, parent_root, static_root];
918 self
919 }
920
921 /// Sets whether visibility is enforced, and whether private items of a
922 /// module are visible to its descendant modules.
923 ///
924 /// # Examples
925 ///
926 /// ```
927 /// use resolve_lang::Policy;
928 ///
929 /// let p = Policy::new().with_visibility(true, false);
930 /// assert!(p.visibility_enforced());
931 /// assert!(!p.private_to_descendants());
932 /// ```
933 #[must_use]
934 pub const fn with_visibility(mut self, enforce: bool, private_to_descendants: bool) -> Self {
935 self.enforce_visibility = enforce;
936 self.private_to_descendants = private_to_descendants;
937 self
938 }
939
940 /// Sets which import forms the language has, and how imports re-export.
941 ///
942 /// # Examples
943 ///
944 /// ```
945 /// use resolve_lang::{Policy, Reexport};
946 ///
947 /// let p = Policy::new().with_imports(false, true, Reexport::Never);
948 /// assert!(!p.globs_allowed());
949 /// assert!(p.aliases_allowed());
950 /// ```
951 #[must_use]
952 pub const fn with_imports(mut self, globs: bool, aliases: bool, reexport: Reexport) -> Self {
953 self.globs = globs;
954 self.aliases = aliases;
955 self.reexport = reexport;
956 self
957 }
958
959 /// Sets whether a `global` declaration naming no known global binds a host
960 /// global (`Res::Extern`) instead of being reported.
961 ///
962 /// # Examples
963 ///
964 /// ```
965 /// use resolve_lang::Policy;
966 ///
967 /// assert!(Policy::new().with_implicit_globals(true).implicit_globals());
968 /// ```
969 #[must_use]
970 pub const fn with_implicit_globals(mut self, implicit: bool) -> Self {
971 self.implicit_globals = implicit;
972 self
973 }
974
975 // ------------------------------------------------------------ getters
976
977 /// When items of `class` become visible.
978 ///
979 /// # Examples
980 ///
981 /// ```
982 /// use resolve_lang::{Hoist, ItemClass, Policy};
983 ///
984 /// assert_eq!(Policy::new().hoisting(ItemClass::Fn), Hoist::Scope);
985 /// ```
986 #[must_use]
987 pub const fn hoisting(&self, class: ItemClass) -> Hoist {
988 self.hoist[class.index()]
989 }
990
991 /// The namespaces items of `class` define their name in (before merging).
992 ///
993 /// # Examples
994 ///
995 /// ```
996 /// use resolve_lang::{ItemClass, Namespace, Policy};
997 ///
998 /// assert!(Policy::new().occupies(ItemClass::Fn).contains(Namespace::Value));
999 /// ```
1000 #[must_use]
1001 pub const fn occupies(&self, class: ItemClass) -> NsSet {
1002 self.occupies[class.index()]
1003 }
1004
1005 /// The table namespace `ns` is stored in after merging.
1006 ///
1007 /// # Examples
1008 ///
1009 /// ```
1010 /// use resolve_lang::{Namespace, Policy};
1011 ///
1012 /// assert_eq!(Policy::new().table(Namespace::Module), Namespace::Type);
1013 /// assert_eq!(Policy::new().table(Namespace::Value), Namespace::Value);
1014 /// ```
1015 #[must_use]
1016 pub const fn table(&self, ns: Namespace) -> Namespace {
1017 self.merge[ns.index()]
1018 }
1019
1020 /// How the table of `ns` compares names.
1021 ///
1022 /// # Examples
1023 ///
1024 /// ```
1025 /// use resolve_lang::{Case, Namespace, Policy};
1026 ///
1027 /// assert_eq!(Policy::new().case(Namespace::Value), Case::Sensitive);
1028 /// ```
1029 #[must_use]
1030 pub const fn case(&self, ns: Namespace) -> Case {
1031 self.case[self.merge[ns.index()].index()]
1032 }
1033
1034 /// The shadowing rule.
1035 ///
1036 /// # Examples
1037 ///
1038 /// ```
1039 /// use resolve_lang::{Policy, Shadowing};
1040 ///
1041 /// assert_eq!(Policy::new().shadowing(), Shadowing::Allow);
1042 /// ```
1043 #[must_use]
1044 pub const fn shadowing(&self) -> Shadowing {
1045 self.shadowing
1046 }
1047
1048 /// What duplicate definitions mean.
1049 ///
1050 /// # Examples
1051 ///
1052 /// ```
1053 /// use resolve_lang::{Policy, Redefinition};
1054 ///
1055 /// assert_eq!(Policy::php().redefinition(), Redefinition::Error);
1056 /// ```
1057 #[must_use]
1058 pub const fn redefinition(&self) -> Redefinition {
1059 self.redefinition
1060 }
1061
1062 /// What code inside a class sees of its members.
1063 ///
1064 /// # Examples
1065 ///
1066 /// ```
1067 /// use resolve_lang::{ClassScope, Policy};
1068 ///
1069 /// assert_eq!(Policy::new().class_scope(), ClassScope::Lexical);
1070 /// ```
1071 #[must_use]
1072 pub const fn class_scope(&self) -> ClassScope {
1073 self.class_scope
1074 }
1075
1076 /// Whether nested modules see outer names.
1077 ///
1078 /// # Examples
1079 ///
1080 /// ```
1081 /// use resolve_lang::{ModuleScope, Policy};
1082 ///
1083 /// assert_eq!(Policy::php().module_scope(), ModuleScope::Lexical);
1084 /// ```
1085 #[must_use]
1086 pub const fn module_scope(&self) -> ModuleScope {
1087 self.module_scope
1088 }
1089
1090 /// How `self::` binds.
1091 ///
1092 /// # Examples
1093 ///
1094 /// ```
1095 /// use resolve_lang::{Policy, RootBinding};
1096 ///
1097 /// assert_eq!(Policy::python().self_root(), RootBinding::Unsupported);
1098 /// ```
1099 #[must_use]
1100 pub const fn self_root(&self) -> RootBinding {
1101 self.roots[0]
1102 }
1103
1104 /// How `parent::` binds.
1105 ///
1106 /// # Examples
1107 ///
1108 /// ```
1109 /// use resolve_lang::{Policy, RootBinding};
1110 ///
1111 /// assert_eq!(Policy::kraken().parent_root(), RootBinding::Unsupported);
1112 /// ```
1113 #[must_use]
1114 pub const fn parent_root(&self) -> RootBinding {
1115 self.roots[1]
1116 }
1117
1118 /// How `static::` binds.
1119 ///
1120 /// # Examples
1121 ///
1122 /// ```
1123 /// use resolve_lang::{Policy, RootBinding};
1124 ///
1125 /// assert_eq!(Policy::php().static_root(), RootBinding::Late);
1126 /// ```
1127 #[must_use]
1128 pub const fn static_root(&self) -> RootBinding {
1129 self.roots[2]
1130 }
1131
1132 /// Whether visibility (`Vis`) is enforced.
1133 ///
1134 /// # Examples
1135 ///
1136 /// ```
1137 /// use resolve_lang::Policy;
1138 ///
1139 /// assert!(Policy::php().visibility_enforced());
1140 /// ```
1141 #[must_use]
1142 pub const fn visibility_enforced(&self) -> bool {
1143 self.enforce_visibility
1144 }
1145
1146 /// Whether private items of a module are visible in its descendant
1147 /// modules (Rust's rule).
1148 ///
1149 /// # Examples
1150 ///
1151 /// ```
1152 /// use resolve_lang::Policy;
1153 ///
1154 /// assert!(Policy::new().private_to_descendants());
1155 /// ```
1156 #[must_use]
1157 pub const fn private_to_descendants(&self) -> bool {
1158 self.private_to_descendants
1159 }
1160
1161 /// Whether glob imports are allowed.
1162 ///
1163 /// # Examples
1164 ///
1165 /// ```
1166 /// use resolve_lang::Policy;
1167 ///
1168 /// assert!(Policy::python().globs_allowed());
1169 /// ```
1170 #[must_use]
1171 pub const fn globs_allowed(&self) -> bool {
1172 self.globs
1173 }
1174
1175 /// Whether aliased imports (`use x as y`) are allowed.
1176 ///
1177 /// # Examples
1178 ///
1179 /// ```
1180 /// use resolve_lang::Policy;
1181 ///
1182 /// assert!(Policy::php().aliases_allowed());
1183 /// ```
1184 #[must_use]
1185 pub const fn aliases_allowed(&self) -> bool {
1186 self.aliases
1187 }
1188
1189 /// How imports re-export.
1190 ///
1191 /// # Examples
1192 ///
1193 /// ```
1194 /// use resolve_lang::{Policy, Reexport};
1195 ///
1196 /// assert_eq!(Policy::php().reexport(), Reexport::Never);
1197 /// ```
1198 #[must_use]
1199 pub const fn reexport(&self) -> Reexport {
1200 self.reexport
1201 }
1202
1203 /// Whether an unknown `global` name binds a host global.
1204 ///
1205 /// # Examples
1206 ///
1207 /// ```
1208 /// use resolve_lang::Policy;
1209 ///
1210 /// assert!(!Policy::kraken().implicit_globals());
1211 /// ```
1212 #[must_use]
1213 pub const fn implicit_globals(&self) -> bool {
1214 self.implicit_globals
1215 }
1216
1217 /// The export visibility of an import whose item says `declared`.
1218 pub(crate) const fn import_vis(&self, declared: Vis) -> Vis {
1219 match self.reexport {
1220 Reexport::AsDeclared => declared,
1221 Reexport::Always => Vis::Public,
1222 Reexport::Never => Vis::Private,
1223 }
1224 }
1225
1226 /// The table index (`0..TABLES`) of namespace `ns`.
1227 #[inline]
1228 pub(crate) const fn table_ix(&self, ns: Namespace) -> u8 {
1229 self.merge[ns.index()] as u8
1230 }
1231
1232 /// Whether table `t` (a table index) folds case.
1233 #[inline]
1234 pub(crate) const fn table_folds(&self, t: u8) -> bool {
1235 (t as usize) < TABLES && !matches!(self.case[t as usize], Case::Sensitive)
1236 }
1237
1238 /// Whether any table folds case.
1239 pub(crate) const fn any_folds(&self) -> bool {
1240 let mut t = 0u8;
1241 while (t as usize) < TABLES {
1242 if self.table_folds(t) {
1243 return true;
1244 }
1245 t += 1;
1246 }
1247 false
1248 }
1249}
1250
1251/// The number of name tables (one per [`Namespace`]).
1252pub(crate) const TABLES: usize = 6;
1253
1254/// The rank of a visibility, for "the smaller of two" (glob re-exports).
1255pub(crate) const fn vis_rank(v: Vis) -> u8 {
1256 match v {
1257 Vis::Private => 0,
1258 Vis::Protected => 1,
1259 Vis::Package => 2,
1260 Vis::Public => 3,
1261 }
1262}
1263
1264/// The narrower of two visibilities.
1265pub(crate) const fn min_vis(a: Vis, b: Vis) -> Vis {
1266 if vis_rank(a) <= vis_rank(b) { a } else { b }
1267}
1268
1269#[cfg(test)]
1270mod tests {
1271 use super::*;
1272
1273 #[test]
1274 fn test_merge_is_transitive() {
1275 let p = Policy::new()
1276 .with_merge(Namespace::Macro, Namespace::Value)
1277 .with_merge(Namespace::Value, Namespace::Type);
1278 assert_eq!(p.table(Namespace::Macro), Namespace::Type);
1279 assert_eq!(p.table(Namespace::Value), Namespace::Type);
1280 assert_eq!(p.table(Namespace::Module), Namespace::Type);
1281 assert_eq!(p.table(Namespace::Label), Namespace::Label);
1282 }
1283
1284 #[test]
1285 fn test_min_vis_orders_private_lowest() {
1286 assert_eq!(min_vis(Vis::Public, Vis::Private), Vis::Private);
1287 assert_eq!(min_vis(Vis::Package, Vis::Public), Vis::Package);
1288 assert_eq!(min_vis(Vis::Protected, Vis::Package), Vis::Protected);
1289 }
1290
1291 #[test]
1292 fn test_import_vis_follows_reexport() {
1293 assert_eq!(Policy::new().import_vis(Vis::Public), Vis::Public);
1294 assert_eq!(Policy::new().import_vis(Vis::Private), Vis::Private);
1295 assert_eq!(Policy::python().import_vis(Vis::Private), Vis::Public);
1296 assert_eq!(Policy::php().import_vis(Vis::Public), Vis::Private);
1297 }
1298
1299 #[test]
1300 fn test_namespace_index_round_trips() {
1301 for ns in Namespace::ALL {
1302 assert_eq!(Namespace::from_index(ns.index()), ns);
1303 }
1304 }
1305}