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}