Skip to main content

solar_sema/ty/
mod.rs

1use crate::{
2    Source, Sources, ast,
3    ast_lowering::SymbolResolver,
4    builtins::{Builtin, members},
5    hir::{self, Hir, SourceId},
6};
7use alloy_primitives::{B256, Selector, U256, keccak256};
8use either::Either;
9use solar_ast::{DataLocation, StateMutability, TypeSize, UserDefinableOperator, Visibility};
10use solar_data_structures::{
11    BumpExt,
12    fmt::{from_fn, or_list},
13    map::{FxBuildHasher, FxHashMap, FxHashSet},
14    smallvec::SmallVec,
15    trustme,
16};
17use solar_interface::{
18    Ident, Session, Span, Symbol,
19    config::CompilerStage,
20    diagnostics::{DiagCtxt, ErrorGuaranteed},
21    source_map::{FileName, SourceFile},
22};
23use std::{
24    fmt,
25    hash::Hash,
26    ops::ControlFlow,
27    sync::{
28        Arc, OnceLock,
29        atomic::{AtomicUsize, Ordering},
30    },
31};
32use thread_local::ThreadLocal;
33
34mod print;
35pub use print::{TyAbiPrinter, TyAbiPrinterMode};
36
37mod common;
38pub use common::{CommonTypes, EachDataLoc};
39
40mod interner;
41use interner::Interner;
42
43#[allow(clippy::module_inception)]
44mod ty;
45pub(crate) use ty::SameSourceFileLevelUserTypeError;
46pub use ty::{Ty, TyConvertError, TyData, TyFlags, TyFn, TyFnKind, TyKind};
47
48type FxOnceMap<K, V> = once_map::OnceMap<K, V, FxBuildHasher>;
49type NatSpecContractKey = (Symbol, hir::SourceId);
50type UsingDirectiveKey = usize;
51
52/// A function exported by a contract.
53#[derive(Clone, Copy, Debug)]
54pub struct InterfaceFunction<'gcx> {
55    /// The function ID.
56    pub id: hir::FunctionId,
57    /// The function 4-byte selector.
58    pub selector: Selector,
59    /// The function type.
60    pub ty: Ty<'gcx>,
61}
62
63/// List of all the functions exported by a contract.
64///
65/// Return type of [`Gcx::interface_functions`].
66#[derive(Clone, Copy, Debug)]
67pub struct InterfaceFunctions<'gcx> {
68    /// The exported functions along with their selector.
69    pub functions: &'gcx [InterfaceFunction<'gcx>],
70    /// The index in `functions` where the inherited functions start.
71    pub inheritance_start: usize,
72}
73
74/// Sparse results produced by semantic type checking for later compiler stages.
75#[derive(Clone, Debug, Default)]
76pub struct TypeckResults<'gcx> {
77    pub(crate) expr_types: FxHashMap<hir::ExprId, Ty<'gcx>>,
78    pub(crate) resolved_callees: FxHashMap<hir::ExprId, ResolvedCallee>,
79    pub(crate) resolved_members: FxHashMap<hir::ExprId, ResolvedMember>,
80    pub(crate) unsupported_udvt_operators: FxHashSet<hir::ExprId>,
81}
82
83/// The target selected for a call callee expression.
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
85pub struct ResolvedCallee {
86    pub res: hir::Res,
87    /// Whether this member call was attached to the receiver through `using for`.
88    pub attached: bool,
89}
90
91impl ResolvedCallee {
92    #[inline]
93    pub fn new(res: hir::Res, attached: bool) -> Self {
94        Self { res, attached }
95    }
96}
97
98/// The target selected for a non-call member access expression.
99#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
100pub enum ResolvedMember {
101    /// A member with a regular item or builtin resolution.
102    Res(hir::Res),
103    /// A struct field selected from the receiver type.
104    StructField { struct_id: hir::StructId, field_index: usize },
105    /// An enum variant selected from `Enum.Variant`.
106    EnumVariant { enum_id: hir::EnumId, variant_index: usize },
107}
108
109/// A completion candidate for a member visible on a receiver type.
110#[derive(Clone, Copy, Debug)]
111pub struct MemberCompletion<'gcx> {
112    /// The visible member candidate.
113    pub member: members::Member<'gcx>,
114    /// The source-level target for the member, if one can be identified.
115    pub resolved: Option<ResolvedMember>,
116}
117
118/// Parameter names available for a callable's visible arguments.
119pub type CallableParamNames = SmallVec<[Option<Symbol>; 8]>;
120
121/// The source declaration used for a callable's parameter names.
122#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123pub enum CallableParamSource {
124    /// A function-like declaration.
125    Function {
126        /// The function ID.
127        id: hir::FunctionId,
128        /// Whether the leading receiver parameter is not visible at the call site.
129        ///
130        /// Attached member calls strip the receiver from the visible call parameters,
131        /// but named arguments still come from the original function declaration.
132        skips_receiver: bool,
133    },
134    /// A variable declared with a function type.
135    FunctionType(hir::VariableId),
136    /// A struct constructor.
137    Struct(hir::StructId),
138    /// An event invocation.
139    Event(hir::EventId),
140    /// An error invocation.
141    Error(hir::ErrorId),
142}
143
144/// The visible signature of a callable expression.
145#[derive(Clone, Copy, Debug)]
146pub struct CallableSignature<'gcx> {
147    /// The visible parameter types at the call site.
148    pub parameters: &'gcx [Ty<'gcx>],
149    /// The return types.
150    pub returns: &'gcx [Ty<'gcx>],
151    /// The declaration source for parameter names, if one exists.
152    pub param_source: Option<CallableParamSource>,
153}
154
155impl<'gcx> TypeckResults<'gcx> {
156    /// Returns the type inferred for the given expression, if available.
157    #[inline]
158    pub fn type_of_expr(&self, id: hir::ExprId) -> Option<Ty<'gcx>> {
159        self.expr_types.get(&id).copied()
160    }
161
162    /// Returns the overload/member target selected for a call callee expression, if available.
163    #[inline]
164    pub fn resolved_callee(&self, id: hir::ExprId) -> Option<ResolvedCallee> {
165        self.resolved_callees.get(&id).copied()
166    }
167
168    /// Returns the target selected for a non-call member access expression, if available.
169    #[inline]
170    pub fn resolved_member(&self, id: hir::ExprId) -> Option<ResolvedMember> {
171        self.resolved_members.get(&id).copied()
172    }
173
174    /// Returns the selected builtin target for a non-call member access expression, if available.
175    #[inline]
176    pub fn builtin_member(&self, id: hir::ExprId) -> Option<Builtin> {
177        match self.resolved_member(id)? {
178            ResolvedMember::Res(hir::Res::Builtin(builtin)) => Some(builtin),
179            _ => None,
180        }
181    }
182
183    /// Returns the selected builtin target for a call callee expression, if available.
184    #[inline]
185    pub fn builtin_callee(&self, id: hir::ExprId) -> Option<Builtin> {
186        match self.resolved_callee(id)?.res {
187            hir::Res::Builtin(builtin) => Some(builtin),
188            _ => None,
189        }
190    }
191
192    /// Returns whether codegen cannot lower the user-defined operator used by this expression.
193    #[inline]
194    pub fn unsupported_udvt_operator(&self, id: hir::ExprId) -> bool {
195        self.unsupported_udvt_operators.contains(&id)
196    }
197}
198
199impl<'gcx> InterfaceFunctions<'gcx> {
200    /// Returns all the functions.
201    pub fn all(&self) -> &'gcx [InterfaceFunction<'gcx>] {
202        self.functions
203    }
204
205    /// Returns the defined functions.
206    pub fn own(&self) -> &'gcx [InterfaceFunction<'gcx>] {
207        &self.functions[..self.inheritance_start]
208    }
209
210    /// Returns the inherited functions.
211    pub fn inherited(&self) -> &'gcx [InterfaceFunction<'gcx>] {
212        &self.functions[self.inheritance_start..]
213    }
214}
215
216impl<'gcx> std::ops::Deref for InterfaceFunctions<'gcx> {
217    type Target = &'gcx [InterfaceFunction<'gcx>];
218
219    #[inline]
220    fn deref(&self) -> &Self::Target {
221        &self.functions
222    }
223}
224
225impl<'gcx> IntoIterator for InterfaceFunctions<'gcx> {
226    type Item = &'gcx InterfaceFunction<'gcx>;
227    type IntoIter = std::slice::Iter<'gcx, InterfaceFunction<'gcx>>;
228
229    #[inline]
230    fn into_iter(self) -> Self::IntoIter {
231        self.functions.iter()
232    }
233}
234
235/// Recursiveness of a type.
236#[derive(Clone, Copy, Debug)]
237pub enum Recursiveness {
238    /// Not recursive.
239    None,
240    /// Recursive through indirection.
241    Recursive,
242    /// Recursive through direct reference. An error has already been emitted.
243    Infinite(ErrorGuaranteed),
244}
245
246impl Recursiveness {
247    /// Returns `true` if the type is not recursive.
248    #[inline]
249    pub fn is_none(self) -> bool {
250        matches!(self, Self::None)
251    }
252
253    /// Returns `true` if the type is recursive.
254    #[inline]
255    pub fn is_recursive(self) -> bool {
256        !self.is_none()
257    }
258}
259
260/// Reference to the [global context](GlobalCtxt).
261#[derive(Clone, Copy)]
262#[cfg_attr(feature = "nightly", rustc_pass_by_value)]
263pub struct Gcx<'gcx>(&'gcx GlobalCtxt<'gcx>);
264
265impl<'gcx> std::ops::Deref for Gcx<'gcx> {
266    type Target = &'gcx GlobalCtxt<'gcx>;
267
268    #[inline(always)]
269    fn deref(&self) -> &Self::Target {
270        &self.0
271    }
272}
273
274impl<'gcx> fmt::Debug for Gcx<'gcx> {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        self.0.fmt(f)
277    }
278}
279
280/// Transparent wrapper around `&'gcx mut GlobalCtxt<'gcx>`.
281///
282/// This uses a raw pointer because using `&mut` directly would make `'gcx` covariant and this just
283/// is too annoying/impossible to deal with.
284/// Since it's only used internally (`pub(crate)`), this is fine.
285#[repr(transparent)]
286pub(crate) struct GcxMut<'gcx>(*mut GlobalCtxt<'gcx>);
287
288impl<'gcx> GcxMut<'gcx> {
289    #[inline(always)]
290    pub(crate) fn new(gcx: &mut GlobalCtxt<'gcx>) -> Self {
291        Self(gcx)
292    }
293
294    #[inline(always)]
295    pub(crate) fn get(&self) -> Gcx<'gcx> {
296        unsafe { Gcx(&*self.0) }
297    }
298
299    #[inline(always)]
300    pub(crate) fn get_mut(&mut self) -> &'gcx mut GlobalCtxt<'gcx> {
301        unsafe { &mut *self.0 }
302    }
303}
304
305impl<'gcx> std::ops::Deref for GcxMut<'gcx> {
306    type Target = &'gcx mut GlobalCtxt<'gcx>;
307
308    #[inline(always)]
309    fn deref(&self) -> &Self::Target {
310        unsafe { core::mem::transmute(self) }
311    }
312}
313
314impl<'gcx> std::ops::DerefMut for GcxMut<'gcx> {
315    #[inline(always)]
316    fn deref_mut(&mut self) -> &mut Self::Target {
317        unsafe { core::mem::transmute(self) }
318    }
319}
320
321#[cfg(test)]
322fn _gcx_traits() {
323    fn assert_send_sync<T: Send + Sync>() {}
324    assert_send_sync::<Gcx<'static>>();
325}
326
327struct AtomicCompilerStage(AtomicUsize);
328
329impl AtomicCompilerStage {
330    fn new() -> Self {
331        Self(AtomicUsize::new(usize::MAX))
332    }
333
334    fn set(&self, stage: CompilerStage) {
335        self.0.store(stage as usize, Ordering::Relaxed);
336    }
337
338    fn get(&self) -> Option<CompilerStage> {
339        let stage = self.0.load(Ordering::Relaxed);
340        if stage == usize::MAX { None } else { Some(CompilerStage::from_repr(stage).unwrap()) }
341    }
342}
343
344/// The global compilation context.
345pub struct GlobalCtxt<'gcx> {
346    pub sess: &'gcx Session,
347    pub sources: Sources<'gcx>,
348    pub(crate) symbol_resolver: SymbolResolver<'gcx>,
349    pub hir: Hir<'gcx>,
350    stage: AtomicCompilerStage,
351
352    pub types: CommonTypes<'gcx>,
353    typeck_results: OnceLock<TypeckResults<'gcx>>,
354
355    pub(crate) ast_arenas: ThreadLocal<ast::Arena>,
356    pub(crate) hir_arenas: ThreadLocal<hir::Arena>,
357    interner: Interner<'gcx>,
358    cache: Cache<'gcx>,
359    pub(crate) inherited_override_functions:
360        FxOnceMap<hir::ContractId, &'gcx crate::typeck::override_checker::InheritedFunctions<'gcx>>,
361}
362
363impl fmt::Debug for GlobalCtxt<'_> {
364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        f.debug_struct("GlobalCtxt")
366            .field("stage", &self.stage.get())
367            .field("sess", self.sess)
368            .field("sources", &self.sources.len())
369            .finish_non_exhaustive()
370    }
371}
372
373impl<'gcx> GlobalCtxt<'gcx> {
374    pub(crate) fn new(sess: &'gcx Session) -> Self {
375        let interner = Interner::new();
376        let hir_arenas = ThreadLocal::<hir::Arena>::new();
377        Self {
378            sess,
379            sources: Sources::new(),
380            symbol_resolver: SymbolResolver::new(&sess.dcx),
381            hir: Hir::new(),
382            stage: AtomicCompilerStage::new(),
383
384            // SAFETY: stable address because ThreadLocal holds the arenas through indirection.
385            types: CommonTypes::new(
386                &interner,
387                unsafe { trustme::decouple_lt(&hir_arenas) }.get_or_default().bump(),
388            ),
389            typeck_results: Default::default(),
390
391            ast_arenas: ThreadLocal::new(),
392            hir_arenas,
393            interner,
394            cache: Cache::default(),
395            inherited_override_functions: FxOnceMap::default(),
396        }
397    }
398}
399
400impl<'gcx> Gcx<'gcx> {
401    pub(crate) fn new(gcx: &'gcx GlobalCtxt<'gcx>) -> Self {
402        Self(gcx)
403    }
404
405    /// Returns the current compiler stage.
406    pub fn stage(&self) -> Option<CompilerStage> {
407        self.stage.get()
408    }
409
410    pub(crate) fn advance_stage(&self, to: CompilerStage) -> ControlFlow<()> {
411        let from = self.stage();
412        let result = self.advance_stage_(to);
413        trace!(?from, ?to, ?result, "advance stage");
414        result
415    }
416
417    fn advance_stage_(&self, to: CompilerStage) -> ControlFlow<()> {
418        let current = self.stage();
419
420        // Special case: allow calling `parse` multiple times while currently parsing.
421        if to == CompilerStage::Parsing && current == Some(to) {
422            return ControlFlow::Continue(());
423        }
424
425        let next = CompilerStage::next_opt(current);
426        if next.is_none_or(|next| to != next) {
427            let current_s = match current {
428                Some(s) => s.to_str(),
429                None => "none",
430            };
431            let next_s = match next {
432                Some(s) => &format!("`{s}`"),
433                None => "none (current stage is the last)",
434            };
435            self.dcx()
436                .bug(format!(
437                    "invalid compiler stage transition: cannot advance from `{current_s}` to `{to}`"
438                ))
439                .note(format!("expected next stage: {next_s}"))
440                .note("stages must be advanced sequentially")
441                .emit();
442        }
443
444        if let Some(current) = current
445            && self.sess.stop_after(current)
446        {
447            return ControlFlow::Break(());
448        }
449
450        self.stage.set(to);
451        ControlFlow::Continue(())
452    }
453
454    /// Returns the diagnostics context.
455    pub fn dcx(self) -> &'gcx DiagCtxt {
456        &self.sess.dcx
457    }
458
459    pub fn arena(self) -> &'gcx hir::Arena {
460        self.hir_arenas.get_or_default()
461    }
462
463    pub fn bump(self) -> &'gcx bumpalo::Bump {
464        self.arena().bump()
465    }
466
467    pub fn alloc<T>(self, value: T) -> &'gcx T {
468        self.bump().alloc(value)
469    }
470
471    pub fn mk_ty(self, kind: TyKind<'gcx>) -> Ty<'gcx> {
472        self.interner.intern_ty(self.bump(), kind)
473    }
474
475    pub fn mk_tys(self, tys: &[Ty<'gcx>]) -> &'gcx [Ty<'gcx>] {
476        self.interner.intern_tys(self.bump(), tys)
477    }
478
479    pub fn mk_ty_iter(self, tys: impl Iterator<Item = Ty<'gcx>>) -> &'gcx [Ty<'gcx>] {
480        self.interner.intern_ty_iter(self.bump(), tys)
481    }
482
483    pub fn mk_ty_tuple(self, tys: &'gcx [Ty<'gcx>]) -> Ty<'gcx> {
484        self.mk_ty(TyKind::Tuple(tys))
485    }
486
487    pub(crate) fn mk_item_tys<T: Into<hir::ItemId> + Copy>(self, ids: &[T]) -> &'gcx [Ty<'gcx>] {
488        self.mk_ty_iter(ids.iter().map(|&id| self.type_of_item(id.into())))
489    }
490
491    pub fn mk_ty_string_literal(self, s: &[u8]) -> Ty<'gcx> {
492        self.mk_ty(TyKind::StringLiteral(
493            std::str::from_utf8(s).is_ok(),
494            TypeSize::new_int_bits(s.len().min(32) as u16 * 8),
495        ))
496    }
497
498    pub fn mk_ty_int_literal(self, negative: bool, bits: u64) -> Option<Ty<'gcx>> {
499        self.mk_ty_int_literal_with_fixed_bytes(negative, bits, None)
500    }
501
502    pub fn mk_ty_int_literal_with_fixed_bytes(
503        self,
504        negative: bool,
505        bits: u64,
506        compatible_fixed_bytes: Option<TypeSize>,
507    ) -> Option<Ty<'gcx>> {
508        let bits = bits.max(1);
509        if bits > TypeSize::MAX as u64 {
510            return None;
511        }
512        Some(self.mk_ty(TyKind::IntLiteral(
513            negative,
514            TypeSize::new_literal_bits(bits as u16),
515            compatible_fixed_bytes,
516        )))
517    }
518
519    pub fn mk_ty_fn(self, ptr: TyFn<'gcx>) -> Ty<'gcx> {
520        self.mk_ty(TyKind::Fn(self.interner.intern_ty_fn(self.bump(), ptr)))
521    }
522
523    /// Returns the type inferred for the given expression, if available.
524    ///
525    /// Expression types are populated by the experimental type checker, which runs when `-Ztypeck`
526    /// or `-Zcodegen` is enabled, or when a codegen output was requested.
527    #[inline]
528    pub fn type_of_expr(self, id: hir::ExprId) -> Option<Ty<'gcx>> {
529        self.typeck_results.get()?.type_of_expr(id)
530    }
531
532    /// Returns the overload/member target selected for a call callee expression, if available.
533    #[inline]
534    pub fn resolved_callee(self, id: hir::ExprId) -> Option<ResolvedCallee> {
535        self.typeck_results.get()?.resolved_callee(id)
536    }
537
538    /// Returns the target selected for a non-call member access expression, if available.
539    #[inline]
540    pub fn resolved_member(self, id: hir::ExprId) -> Option<ResolvedMember> {
541        self.typeck_results.get()?.resolved_member(id)
542    }
543
544    /// Returns the selected builtin target for a non-call member access expression, if available.
545    #[inline]
546    pub fn builtin_member(self, id: hir::ExprId) -> Option<Builtin> {
547        self.typeck_results.get()?.builtin_member(id)
548    }
549
550    /// Returns the selected builtin target for a call callee expression, if available.
551    #[inline]
552    pub fn builtin_callee(self, id: hir::ExprId) -> Option<Builtin> {
553        self.typeck_results.get()?.builtin_callee(id)
554    }
555
556    /// Returns whether codegen cannot lower the user-defined operator used by this expression.
557    #[inline]
558    pub fn unsupported_udvt_operator(self, id: hir::ExprId) -> bool {
559        self.typeck_results.get().is_some_and(|results| results.unsupported_udvt_operator(id))
560    }
561
562    /// Returns whether sparse type-checker results are available for codegen queries.
563    #[inline]
564    pub fn has_typeck_results(self) -> bool {
565        self.typeck_results.get().is_some()
566    }
567
568    pub(crate) fn set_typeck_results(self, results: TypeckResults<'gcx>) {
569        if self.typeck_results.set(results).is_err() {
570            self.dcx().bug("typeck results are already initialized").emit();
571        }
572    }
573
574    pub fn mk_ty_variadic(self) -> Ty<'gcx> {
575        self.mk_ty(TyKind::Variadic)
576    }
577
578    pub fn mk_ty_fn_with_kind(
579        self,
580        kind: TyFnKind,
581        parameters: &[Ty<'gcx>],
582        state_mutability: StateMutability,
583        returns: &[Ty<'gcx>],
584    ) -> Ty<'gcx> {
585        self.mk_ty_fn(TyFn {
586            kind,
587            parameters: self.mk_tys(parameters),
588            returns: self.mk_tys(returns),
589            state_mutability: fn_state_mutability(kind, state_mutability),
590            function_id: None,
591            attached: false,
592        })
593    }
594
595    pub(crate) fn mk_builtin_fn(
596        self,
597        parameters: &[Ty<'gcx>],
598        state_mutability: StateMutability,
599        returns: &[Ty<'gcx>],
600    ) -> Ty<'gcx> {
601        self.mk_ty_fn_with_kind(TyFnKind::Internal, parameters, state_mutability, returns)
602    }
603
604    pub(crate) fn mk_yul_builtin_fn(self, parameters: usize, returns: usize) -> Ty<'gcx> {
605        let parameters = vec![self.types.uint(256); parameters];
606        let returns = vec![self.types.uint(256); returns];
607        self.mk_builtin_fn(&parameters, StateMutability::NonPayable, &returns)
608    }
609
610    pub(crate) fn mk_creation_fn(
611        self,
612        parameters: &[Ty<'gcx>],
613        state_mutability: StateMutability,
614        returns: &[Ty<'gcx>],
615    ) -> Ty<'gcx> {
616        self.mk_ty_fn_with_kind(TyFnKind::Creation, parameters, state_mutability, returns)
617    }
618
619    pub(crate) fn mk_builtin_mod(self, builtin: Builtin) -> Ty<'gcx> {
620        self.mk_ty(TyKind::BuiltinModule(builtin))
621    }
622
623    pub fn mk_ty_misc_err(self) -> Ty<'gcx> {
624        if let Err(e) = self.dcx().has_errors() {
625            self.mk_ty_err(e)
626        } else {
627            self.dcx().bug("mk_ty_misc_err: no errors").emit()
628        }
629    }
630
631    #[inline]
632    pub fn mk_ty_err(self, guar: ErrorGuaranteed) -> Ty<'gcx> {
633        const { assert!(std::mem::size_of::<ErrorGuaranteed>() == 0) }
634        let _ = guar;
635        self.types.__err_do_not_use
636    }
637
638    /// Returns the source file with the given path, if it exists.
639    pub fn get_file(self, name: impl Into<FileName>) -> Option<Arc<SourceFile>> {
640        self.sess.source_map().get_file(name)
641    }
642
643    /// Returns the AST source at the given path, if it exists.
644    pub fn get_ast_source(
645        self,
646        name: impl Into<FileName>,
647    ) -> Option<(SourceId, &'gcx Source<'gcx>)> {
648        let file = self.get_file(name)?;
649        self.sources.get_file(&file)
650    }
651
652    /// Returns the HIR source at the given path, if it exists.
653    pub fn get_hir_source(
654        self,
655        name: impl Into<FileName>,
656    ) -> Option<(SourceId, &'gcx hir::Source<'gcx>)> {
657        let file = self.get_file(name)?;
658        self.hir.sources.iter_enumerated().find(|(_, source)| Arc::ptr_eq(&source.file, &file))
659    }
660
661    /// Returns the name of the given item.
662    ///
663    /// # Panics
664    ///
665    /// Panics if the item has no name, such as unnamed function parameters.
666    pub fn item_name(self, id: impl Into<hir::ItemId>) -> Ident {
667        let id = id.into();
668        self.item_name_opt(id).unwrap_or_else(|| panic!("item_name: missing name for item {id:?}"))
669    }
670
671    /// Returns the canonical name of the given item.
672    ///
673    /// This is the name of the item prefixed by the name of the contract it belongs to.
674    pub fn item_canonical_name(self, id: impl Into<hir::ItemId>) -> impl fmt::Display {
675        self.item_canonical_name_(id.into())
676    }
677    fn item_canonical_name_(self, id: hir::ItemId) -> impl fmt::Display {
678        let name = self.item_name(id);
679        let contract = self.hir.item(id).contract().map(|id| self.item_name(id));
680        from_fn(move |f| {
681            if let Some(contract) = contract {
682                write!(f, "{contract}.")?;
683            }
684            write!(f, "{name}")
685        })
686    }
687
688    /// Returns the fully qualified name of the contract.
689    pub fn contract_fully_qualified_name(
690        self,
691        id: hir::ContractId,
692    ) -> impl fmt::Display + use<'gcx> {
693        from_fn(move |f| {
694            let c = self.hir.contract(id);
695            let source = self.hir.source(c.source);
696            write!(f, "{}:{}", source.file.name.display(), c.name)
697        })
698    }
699
700    /// Returns an iterator over the fields of the given item.
701    ///
702    /// Accepts structs, functions, errors, and events.
703    pub fn item_fields(
704        self,
705        id: impl Into<hir::ItemId>,
706    ) -> impl Iterator<Item = (Ty<'gcx>, hir::VariableId)> {
707        self.item_fields_(id.into())
708    }
709
710    fn item_fields_(self, id: hir::ItemId) -> impl Iterator<Item = (Ty<'gcx>, hir::VariableId)> {
711        let tys = if let hir::ItemId::Struct(id) = id {
712            self.struct_field_types(id)
713        } else {
714            self.item_parameter_types(id)
715        };
716        let params = self.item_parameters(id);
717        debug_assert_eq!(tys.len(), params.len());
718        std::iter::zip(tys.iter().copied(), params.iter().copied())
719    }
720
721    /// Returns the parameter variable declarations of the given function-like item.
722    ///
723    /// Also accepts structs.
724    ///
725    /// # Panics
726    ///
727    /// Panics if the item is not a function-like item or a struct.
728    pub fn item_parameters(self, id: impl Into<hir::ItemId>) -> &'gcx [hir::VariableId] {
729        let id = id.into();
730        self.item_parameters_opt(id)
731            .unwrap_or_else(|| panic!("item_parameters: invalid item {id:?}"))
732    }
733
734    /// Returns the parameter variable declarations of the given function-like item.
735    ///
736    /// Also accepts structs.
737    pub fn item_parameters_opt(
738        self,
739        id: impl Into<hir::ItemId>,
740    ) -> Option<&'gcx [hir::VariableId]> {
741        self.hir.item(id).parameters()
742    }
743
744    /// Returns the return variable declarations of the given function-like item.
745    ///
746    /// # Panics
747    ///
748    /// Panics if the item is not a function-like item.
749    pub fn item_parameter_types(self, id: impl Into<hir::ItemId>) -> &'gcx [Ty<'gcx>] {
750        let id = id.into();
751        self.item_parameter_types_opt(id)
752            .unwrap_or_else(|| panic!("item_parameter_types: invalid item {id:?}"))
753    }
754
755    /// Returns the return variable declarations of the given function-like item.
756    ///
757    /// # Panics
758    ///
759    /// Panics if the item is not a function-like item.
760    pub fn item_parameter_types_opt(self, id: impl Into<hir::ItemId>) -> Option<&'gcx [Ty<'gcx>]> {
761        self.type_of_item(id.into()).parameters()
762    }
763
764    /// Returns the name of the given item.
765    #[inline]
766    pub fn item_name_opt(self, id: impl Into<hir::ItemId>) -> Option<Ident> {
767        self.hir.item(id).name()
768    }
769
770    /// Returns the span of the given item.
771    #[inline]
772    pub fn item_span(self, id: impl Into<hir::ItemId>) -> Span {
773        self.hir.item(id).span()
774    }
775
776    /// Returns the 4-byte selector of the given item. Only accepts functions and errors.
777    ///
778    /// # Panics
779    ///
780    /// Panics if the item is not a function or error.
781    pub fn function_selector(self, id: impl Into<hir::ItemId>) -> Selector {
782        let id = id.into();
783        assert!(
784            matches!(id, hir::ItemId::Function(_) | hir::ItemId::Error(_)),
785            "function_selector: invalid item {id:?}"
786        );
787        self.item_selector(id)[..4].try_into().unwrap()
788    }
789
790    /// Returns the 32-byte selector of the given event.
791    pub fn event_selector(self, id: hir::EventId) -> B256 {
792        self.item_selector(id.into())
793    }
794
795    /// Computes the [`Ty`] of the given [`hir::Type`]. Not cached.
796    pub fn type_of_hir_ty(self, ty: &hir::Type<'_>) -> Ty<'gcx> {
797        let kind = match ty.kind {
798            hir::TypeKind::Elementary(ty) => TyKind::Elementary(ty),
799            hir::TypeKind::Array(array) => {
800                let elem = self.type_of_hir_ty(&array.element);
801                match array.size {
802                    Some(size) => match crate::eval::eval_array_len(self, size) {
803                        Ok(size) => TyKind::Array(elem, size),
804                        Err(guar) => TyKind::Array(self.mk_ty_err(guar), U256::from(1)),
805                    },
806                    None => TyKind::DynArray(elem),
807                }
808            }
809            hir::TypeKind::Function(f) => {
810                let kind = if f.visibility == Visibility::External {
811                    TyFnKind::External
812                } else {
813                    TyFnKind::Internal
814                };
815                return self.mk_ty_fn(TyFn {
816                    kind,
817                    parameters: self.mk_item_tys(f.parameters),
818                    returns: self.mk_item_tys(f.returns),
819                    state_mutability: fn_state_mutability(kind, f.state_mutability),
820                    function_id: None,
821                    attached: false,
822                });
823            }
824            hir::TypeKind::Mapping(mapping) => {
825                let key = self.type_of_hir_ty(&mapping.key);
826                let value = self.type_of_hir_ty(&mapping.value);
827                TyKind::Mapping(key, value)
828            }
829            hir::TypeKind::Custom(item) => return self.type_of_item_simple(item, ty.span),
830            hir::TypeKind::Err(guar) => return self.mk_ty_err(guar),
831        };
832        self.mk_ty(kind)
833    }
834
835    /// Returns the target type of the given [`hir::UsingDirective`].
836    pub(crate) fn type_of_using_directive(
837        self,
838        using: &'gcx hir::UsingDirective<'gcx>,
839    ) -> Option<Ty<'gcx>> {
840        self.type_of_using_directive_cached(using as *const _ as UsingDirectiveKey)
841    }
842
843    fn type_of_item_simple(self, id: hir::ItemId, span: Span) -> Ty<'gcx> {
844        match id {
845            hir::ItemId::Contract(_)
846            | hir::ItemId::Struct(_)
847            | hir::ItemId::Enum(_)
848            | hir::ItemId::Udvt(_) => self.type_of_item(id),
849            _ => {
850                let msg = "name has to refer to a valid user-defined type";
851                self.mk_ty_err(self.dcx().emit_err(span, msg))
852            }
853        }
854    }
855
856    /// Returns the type of the given [`hir::Res`].
857    pub fn type_of_res(self, res: hir::Res) -> Ty<'gcx> {
858        match res {
859            hir::Res::Item(id) => {
860                let ty = self.type_of_item(id);
861                if is_value_ns(id) { ty } else { self.mk_ty(TyKind::Type(ty)) }
862            }
863            hir::Res::Namespace(id) => self.mk_ty(TyKind::Module(id)),
864            hir::Res::Builtin(builtin) => builtin.ty(self),
865            hir::Res::Err(guar) => self.mk_ty_err(guar),
866        }
867    }
868
869    /// Returns the visible callable signature for the given type.
870    pub fn callable_signature_of_ty(self, ty: Ty<'gcx>) -> Option<CallableSignature<'gcx>> {
871        match ty.kind {
872            TyKind::Fn(function_ty) => Some(CallableSignature {
873                parameters: function_ty.parameters,
874                returns: function_ty.returns,
875                param_source: self.callable_param_source_for_fn(function_ty),
876            }),
877            TyKind::Event(parameters, id) => Some(CallableSignature {
878                parameters,
879                returns: Default::default(),
880                param_source: Some(CallableParamSource::Event(id)),
881            }),
882            TyKind::Error(parameters, id) => Some(CallableSignature {
883                parameters,
884                returns: Default::default(),
885                param_source: Some(CallableParamSource::Error(id)),
886            }),
887            TyKind::Type(ty) => self.struct_constructor_signature(ty),
888            TyKind::Err(_) => None,
889            _ => None,
890        }
891    }
892
893    /// Returns the visible callable signature for a member call candidate.
894    pub fn callable_signature_of_member(
895        self,
896        receiver_ty: Ty<'gcx>,
897        member: &members::Member<'gcx>,
898    ) -> Option<CallableSignature<'gcx>> {
899        let TyKind::Fn(function_ty) = member.ty.kind else { return None };
900        let (parameters, skips_receiver) = if member.attached {
901            let (&self_ty, parameters) = function_ty.parameters.split_first()?;
902            if !receiver_ty.convert_implicit_to(self_ty, self) {
903                return None;
904            }
905            (parameters, true)
906        } else {
907            (function_ty.parameters, false)
908        };
909        Some(CallableSignature {
910            parameters,
911            returns: function_ty.returns,
912            param_source: function_ty
913                .function_id
914                .map(|id| CallableParamSource::Function { id, skips_receiver }),
915        })
916    }
917
918    /// Returns the parameter names from a callable parameter source.
919    pub fn callable_param_names(self, source: CallableParamSource) -> CallableParamNames {
920        match source {
921            CallableParamSource::Function { id, skips_receiver } => {
922                let mut names = self.param_names(self.hir.function(id).parameters);
923                if skips_receiver {
924                    debug_assert!(!names.is_empty());
925                    names.remove(0);
926                }
927                names
928            }
929            CallableParamSource::FunctionType(id) => match self.hir.variable(id).ty.kind {
930                hir::TypeKind::Function(ty) => self.param_names(ty.parameters),
931                _ => Default::default(),
932            },
933            CallableParamSource::Struct(id) => self.param_names(self.hir.strukt(id).fields),
934            CallableParamSource::Event(id) => self.param_names(self.hir.event(id).parameters),
935            CallableParamSource::Error(id) => self.param_names(self.hir.error(id).parameters),
936        }
937    }
938
939    fn callable_param_source_for_fn(self, function_ty: &TyFn<'gcx>) -> Option<CallableParamSource> {
940        function_ty.function_id.map(|id| {
941            let declared_param_count = self.hir.function(id).parameters.len();
942            let visible_param_count = function_ty.parameters.len();
943            debug_assert!(
944                declared_param_count == visible_param_count
945                    || declared_param_count == visible_param_count + 1
946            );
947            CallableParamSource::Function {
948                id,
949                skips_receiver: declared_param_count == visible_param_count + 1,
950            }
951        })
952    }
953
954    fn struct_constructor_signature(self, ty: Ty<'gcx>) -> Option<CallableSignature<'gcx>> {
955        let TyKind::Struct(id) = ty.kind else { return None };
956        let parameters = self.mk_ty_iter(
957            self.struct_field_types(id)
958                .iter()
959                .map(|&field_ty| field_ty.with_loc_if_ref(self, DataLocation::Memory)),
960        );
961        let returns = self.mk_ty_iter(std::iter::once(ty.with_loc(self, DataLocation::Memory)));
962        Some(CallableSignature {
963            parameters,
964            returns,
965            param_source: Some(CallableParamSource::Struct(id)),
966        })
967    }
968
969    fn param_names(self, params: &[hir::VariableId]) -> CallableParamNames {
970        params.iter().map(|&id| self.hir.variable(id).name.map(|i| i.name)).collect()
971    }
972
973    /// Returns the type of the given literal.
974    pub fn type_of_lit(self, lit: &'gcx hir::Lit<'gcx>) -> Ty<'gcx> {
975        match &lit.kind {
976            solar_ast::LitKind::Str(_, s, _) => self.mk_ty_string_literal(s.as_byte_str()),
977            solar_ast::LitKind::Number(int) => {
978                let compatible_fixed_bytes = compatible_fixed_bytes_type(lit);
979                self.mk_ty_int_literal_with_fixed_bytes(
980                    false,
981                    int.bit_len() as _,
982                    compatible_fixed_bytes,
983                )
984                .unwrap_or_else(|| {
985                    self.mk_ty_err(
986                        self.dcx().emit_err(lit.span, "integer literal is greater than 2**256"),
987                    )
988                })
989            }
990            solar_ast::LitKind::Rational(_) => {
991                self.mk_ty_err(self.dcx().emit_err(lit.span, "rational literals are not supported"))
992            }
993            solar_ast::LitKind::Address(_) => self.types.address,
994            solar_ast::LitKind::Bool(_) => self.types.bool,
995            &solar_ast::LitKind::Err(guar) => self.mk_ty_err(guar),
996        }
997    }
998
999    pub fn members_of(
1000        self,
1001        ty: Ty<'gcx>,
1002        source: hir::SourceId,
1003        contract: Option<hir::ContractId>,
1004    ) -> impl Iterator<Item = members::Member<'gcx>> + 'gcx {
1005        let native =
1006            self.native_members_in_context(ty, contract).unwrap_or_else(|| self.native_members(ty));
1007        let attached = self.attached_functions(ty, source, contract);
1008        native.iter().copied().chain(attached)
1009    }
1010
1011    pub fn member_completions_of(
1012        self,
1013        ty: Ty<'gcx>,
1014        source: hir::SourceId,
1015        contract: Option<hir::ContractId>,
1016    ) -> impl Iterator<Item = MemberCompletion<'gcx>> + 'gcx {
1017        self.members_of(ty, source, contract).map(move |member| MemberCompletion {
1018            resolved: self.resolve_member_target(ty, member.name, member.res),
1019            member,
1020        })
1021    }
1022
1023    pub(crate) fn resolve_member_target(
1024        self,
1025        receiver_ty: Ty<'gcx>,
1026        name: Symbol,
1027        res: Option<hir::Res>,
1028    ) -> Option<ResolvedMember> {
1029        if let Some(res) = res {
1030            return Some(ResolvedMember::Res(res));
1031        }
1032
1033        match receiver_ty.kind {
1034            TyKind::Ref(inner, _) => {
1035                let TyKind::Struct(struct_id) = inner.kind else { return None };
1036                let field_index = self.struct_field_index(struct_id, name)?;
1037                Some(ResolvedMember::StructField { struct_id, field_index })
1038            }
1039            TyKind::Struct(struct_id) => {
1040                let field_index = self.struct_field_index(struct_id, name)?;
1041                Some(ResolvedMember::StructField { struct_id, field_index })
1042            }
1043            TyKind::Type(ty) => {
1044                let TyKind::Enum(enum_id) = ty.kind else { return None };
1045                let variant_index = self
1046                    .hir
1047                    .enumm(enum_id)
1048                    .variants
1049                    .iter()
1050                    .position(|variant| variant.name == name)?;
1051                Some(ResolvedMember::EnumVariant { enum_id, variant_index })
1052            }
1053            _ => None,
1054        }
1055    }
1056
1057    fn struct_field_index(self, struct_id: hir::StructId, name: Symbol) -> Option<usize> {
1058        self.hir.strukt(struct_id).fields.iter().position(|&field_id| {
1059            self.hir.variable(field_id).name.is_some_and(|field| field.name == name)
1060        })
1061    }
1062
1063    fn native_members_in_context(
1064        self,
1065        ty: Ty<'gcx>,
1066        current_contract: Option<hir::ContractId>,
1067    ) -> Option<members::MemberList<'gcx>> {
1068        let current_contract = current_contract?;
1069        match ty.kind {
1070            TyKind::Type(ty) => {
1071                let TyKind::Contract(id) = ty.kind else { return None };
1072                let contract = self.hir.contract(id);
1073                if contract.kind.is_library()
1074                    || !self.hir.contract(current_contract).linearized_bases.contains(&id)
1075                {
1076                    return None;
1077                }
1078                Some(self.contract_type_members_in_context((id, current_contract)))
1079            }
1080            TyKind::Fn(f) if f.kind == TyFnKind::Internal => {
1081                let id = f.function_id?;
1082                Some(self.internal_function_members_in_context((id, current_contract)))
1083            }
1084            _ => None,
1085        }
1086    }
1087
1088    pub(crate) fn for_each_user_operator(
1089        self,
1090        ty: Ty<'gcx>,
1091        source: hir::SourceId,
1092        contract: Option<hir::ContractId>,
1093        op: UserDefinableOperator,
1094        unary: bool,
1095        f: &mut dyn FnMut(hir::FunctionId),
1096    ) {
1097        let TyKind::Udvt(_, user_ty) = ty.peel_refs().kind else {
1098            return;
1099        };
1100        let ty = self.type_of_item(user_ty.into());
1101        let mut seen = FxHashSet::default();
1102        self.for_each_using_directive_for_type(ty, source, contract, &mut |using| {
1103            for entry in using.entries {
1104                if entry.operator == Some(op)
1105                    && let hir::UsingEntryKind::Functions(candidates) = entry.kind
1106                {
1107                    for &function_id in candidates {
1108                        if let TyKind::Fn(function_ty) = self.type_of_item(function_id.into()).kind
1109                            && function_ty.parameters.len() == if unary { 1 } else { 2 }
1110                            && function_ty.parameters.first().copied() == Some(ty)
1111                            && seen.insert(function_id)
1112                        {
1113                            f(function_id);
1114                        }
1115                    }
1116                }
1117            }
1118        });
1119    }
1120
1121    fn attached_functions(
1122        self,
1123        ty: Ty<'gcx>,
1124        source: hir::SourceId,
1125        contract: Option<hir::ContractId>,
1126    ) -> Vec<members::Member<'gcx>> {
1127        let mut members = Vec::new();
1128        let mut seen = FxHashSet::default();
1129        self.for_each_using_directive_for_type(ty, source, contract, &mut |using| {
1130            for entry in using.entries {
1131                if entry.operator.is_some() {
1132                    continue;
1133                }
1134                match entry.kind {
1135                    hir::UsingEntryKind::Library(library) => {
1136                        for function in self.hir.contract(library).functions() {
1137                            let f = self.hir.function(function);
1138                            if !f.is_ordinary()
1139                                || f.parameters.is_empty()
1140                                || f.visibility == Visibility::Private
1141                            {
1142                                continue;
1143                            }
1144                            self.add_attached_function(ty, function, None, &mut seen, &mut members);
1145                        }
1146                    }
1147                    hir::UsingEntryKind::Functions(functions) => {
1148                        for &function in functions {
1149                            let name = entry.name.unwrap_or_else(|| self.item_name(function).name);
1150                            self.add_attached_function(
1151                                ty,
1152                                function,
1153                                Some(name),
1154                                &mut seen,
1155                                &mut members,
1156                            );
1157                        }
1158                    }
1159                    hir::UsingEntryKind::Err(_) => {}
1160                }
1161            }
1162        });
1163        members
1164    }
1165
1166    fn add_attached_function(
1167        self,
1168        ty: Ty<'gcx>,
1169        function: hir::FunctionId,
1170        name: Option<Symbol>,
1171        seen: &mut FxHashSet<(Symbol, hir::FunctionId)>,
1172        members: &mut Vec<members::Member<'gcx>>,
1173    ) {
1174        let function_item = self.hir.function(function);
1175        let fn_ty = self.type_of_item(function.into());
1176        let fn_ty =
1177            if function_item.contract.is_some_and(|id| self.hir.contract(id).kind.is_library())
1178                && function_item.visibility >= Visibility::Public
1179            {
1180                fn_ty.as_externally_callable_function(true, self)
1181            } else {
1182                fn_ty
1183            }
1184            .as_attached_function(self);
1185        if let TyKind::Fn(function_ty) = fn_ty.kind
1186            && let Some(&self_ty) = function_ty.parameters.first()
1187            && ty.convert_implicit_to(self_ty, self)
1188            && let name = name.unwrap_or_else(|| self.item_name(function).name)
1189            && seen.insert((name, function))
1190        {
1191            members.push(members::Member::with_attached_function(name, fn_ty, function));
1192        }
1193    }
1194
1195    fn for_each_using_directive_for_type(
1196        self,
1197        ty: Ty<'gcx>,
1198        source: hir::SourceId,
1199        contract: Option<hir::ContractId>,
1200        f: &mut dyn FnMut(&'gcx hir::UsingDirective<'gcx>),
1201    ) {
1202        let mut check = |usings: &'gcx [hir::UsingDirective<'gcx>], only_global: bool| {
1203            for using in usings {
1204                if self.using_directive_applies(using, ty, only_global) {
1205                    f(using);
1206                }
1207            }
1208        };
1209
1210        if let Some(contract) = contract {
1211            check(self.hir.contract(contract).usings, false);
1212        }
1213        check(self.hir.source(source).usings, false);
1214
1215        if let Some(type_source) = ty.item_source(self)
1216            && type_source != source
1217        {
1218            check(self.hir.source(type_source).usings, true);
1219        }
1220    }
1221
1222    fn using_directive_applies(
1223        self,
1224        using: &'gcx hir::UsingDirective<'gcx>,
1225        ty: Ty<'gcx>,
1226        only_global: bool,
1227    ) -> bool {
1228        if only_global && !(using.global && using.ty.is_some()) {
1229            return false;
1230        }
1231        let Some(using_ty) = self.type_of_using_directive(using) else {
1232            // For `*`.
1233            return true;
1234        };
1235        let loc = ty.loc().unwrap_or(DataLocation::Storage);
1236        using_directive_ty_matches(ty, using_ty.with_loc_if_ref(self, loc))
1237    }
1238}
1239
1240fn using_directive_ty_matches(ty: Ty<'_>, using_ty: Ty<'_>) -> bool {
1241    if ty == using_ty {
1242        return true;
1243    }
1244    // HACK: allow attached functions to be called on function declarations. Function type equality
1245    // also checks declaration IDs, so this is a quick way to make it work for now.
1246    if let (TyKind::Fn(a), TyKind::Fn(b)) = (ty.kind, using_ty.kind) {
1247        return a.kind == b.kind
1248            && a.parameters == b.parameters
1249            && a.returns == b.returns
1250            && a.state_mutability == b.state_mutability
1251            && a.attached == b.attached;
1252    }
1253    false
1254}
1255
1256fn compatible_fixed_bytes_type(lit: &hir::Lit<'_>) -> Option<TypeSize> {
1257    let solar_ast::LitKind::Number(int) = lit.kind else { return None };
1258    if int.is_zero() {
1259        return Some(TypeSize::ZERO);
1260    }
1261
1262    let hex = lit.symbol.as_str().strip_prefix("0x")?;
1263    let digit_count = hex.bytes().filter(|&b| b != b'_').count();
1264    if digit_count % 2 == 0 {
1265        TypeSize::try_new_fb_bytes((digit_count / 2).try_into().ok()?)
1266    } else {
1267        None
1268    }
1269}
1270
1271fn fn_state_mutability(kind: TyFnKind, state_mutability: StateMutability) -> StateMutability {
1272    if kind == TyFnKind::Internal && state_mutability == StateMutability::Payable {
1273        StateMutability::NonPayable
1274    } else {
1275        state_mutability
1276    }
1277}
1278
1279macro_rules! cached {
1280    ($($(#[$attr:meta])* $vis:vis fn $name:ident($gcx:ident: _, $key:ident : $key_type:ty) -> $value:ty $imp:block)*) => {
1281        #[derive(Default)]
1282        struct Cache<'gcx> {
1283            $(
1284                $name: FxOnceMap<$key_type, $value>,
1285            )*
1286        }
1287
1288        impl<'gcx> Gcx<'gcx> {
1289            $(
1290                $(#[$attr])*
1291                $vis fn $name(self, $key: $key_type) -> $value {
1292                    #[cfg(false)]
1293                    let _guard = log_cache_query(stringify!($name), &$key);
1294                    #[cfg(false)]
1295                    let mut hit = true;
1296                    let r = cache_insert(&self.cache.$name, $key, |&$key| {
1297                        #[cfg(false)]
1298                        {
1299                            hit = false;
1300                        }
1301                        let $gcx = self;
1302                        $imp
1303                    });
1304                    #[cfg(false)]
1305                    log_cache_query_result(&r, hit);
1306                    r
1307                }
1308            )*
1309        }
1310    };
1311}
1312
1313cached! {
1314/// Returns the [ERC-165] interface ID of the given contract.
1315///
1316/// This is the XOR of the selectors of all function selectors in the interface.
1317///
1318/// The solc implementation excludes inheritance: <https://github.com/argotorg/solidity/blob/ad2644c52b3afbe80801322c5fe44edb59383500/libsolidity/ast/AST.cpp#L310-L316>
1319///
1320/// See [ERC-165] for more details.
1321///
1322/// [ERC-165]: https://eips.ethereum.org/EIPS/eip-165
1323pub fn interface_id(gcx: _, id: hir::ContractId) -> Selector {
1324    let kind = gcx.hir.contract(id).kind;
1325    assert!(kind.is_interface(), "{kind} {id:?} is not an interface");
1326    let selectors = gcx.interface_functions(id).own().iter().map(|f| f.selector);
1327    selectors.fold(Selector::ZERO, std::ops::BitXor::bitxor)
1328}
1329
1330/// Returns all the exported functions of the given contract.
1331///
1332/// The contract doesn't have to be an interface.
1333pub fn interface_functions(gcx: _, id: hir::ContractId) -> InterfaceFunctions<'gcx> {
1334    let c = gcx.hir.contract(id);
1335    let mut inheritance_start = None;
1336    let mut signatures_seen = FxHashSet::default();
1337    let mut hash_collisions = FxHashMap::default();
1338    let functions = c.linearized_bases.iter().flat_map(|&base| {
1339        let b = gcx.hir.contract(base);
1340        let functions =
1341            b.functions().filter(|&f| gcx.hir.function(f).is_part_of_external_interface());
1342        if base == id {
1343            assert!(inheritance_start.is_none(), "duplicate self ID in linearized_bases");
1344            inheritance_start = Some(functions.clone().count());
1345        }
1346        functions
1347    }).filter_map(|f_id| {
1348        let f = gcx.hir.function(f_id);
1349        let TyKind::Fn(fn_ty) = gcx.type_of_item(f_id.into()).kind else { unreachable!() };
1350        let ty = gcx
1351            .mk_ty_fn(TyFn {
1352                kind: TyFnKind::External,
1353                parameters: fn_ty.parameters,
1354                returns: fn_ty.returns,
1355                state_mutability: f.state_mutability,
1356                function_id: fn_ty.function_id,
1357                attached: false,
1358            })
1359            .as_externally_callable_function(false, gcx);
1360        let TyKind::Fn(ty_f) = ty.kind else { unreachable!() };
1361        let mut result = Ok(());
1362        for (var_id, ty) in f.variables().zip(ty_f.tys()) {
1363            if let Err(guar) = ty.error_reported() {
1364                result = Err(guar);
1365                continue;
1366            }
1367            if !ty.can_be_exported(gcx) {
1368                // TODO: implement `interfaceType`
1369                if c.kind.is_library() {
1370                    result = Err(ErrorGuaranteed::new_unchecked());
1371                    continue;
1372                }
1373
1374                let kind = f.description();
1375                let msg = if ty.has_mapping(gcx) {
1376                    format!("types containing mappings cannot be parameter or return types of public {kind}s")
1377                } else if ty.is_recursive(gcx) {
1378                    format!("recursive types cannot be parameter or return types of public {kind}s")
1379                } else if ty.has_internal_function() {
1380                    format!("types containing internal function pointers cannot be parameter or return types of public {kind}s")
1381                } else {
1382                    format!("this type cannot be parameter or return type of a public {kind}")
1383                };
1384                let span = gcx.hir.variable(var_id).ty.span;
1385                result = Err(gcx.dcx().emit_err(span, msg));
1386            }
1387        }
1388        if result.is_err() {
1389            return None;
1390        }
1391
1392        // Virtual functions or ones with the same function parameter types are checked separately,
1393        // skip them here to avoid reporting them as selector hash collision errors below.
1394        let hash = gcx.item_selector(f_id.into());
1395        let selector: Selector = hash[..4].try_into().unwrap();
1396        if !signatures_seen.insert(hash) {
1397            return None;
1398        }
1399
1400        // Check for selector hash collisions.
1401        if let Some(prev) = hash_collisions.insert(selector, f_id) {
1402            let f2 = gcx.hir.function(prev);
1403            let msg = "function signature hash collision";
1404            let full_note = format!(
1405                "the function signatures `{}` and `{}` produce the same 4-byte selector `{selector}`",
1406                gcx.item_signature(f_id.into()),
1407                gcx.item_signature(prev.into()),
1408            );
1409            gcx.dcx().err(msg).span(c.name.span).span_note(f.span, "first function").span_note(f2.span, "second function").note(full_note).emit();
1410        }
1411
1412        Some(InterfaceFunction { selector, id: f_id, ty })
1413    });
1414    let functions = gcx.bump().alloc_from_iter(functions);
1415    trace!("{}.interfaceFunctions.len() = {}", gcx.contract_fully_qualified_name(id), functions.len());
1416    let inheritance_start = inheritance_start.expect("linearized_bases did not contain self ID");
1417    InterfaceFunctions { functions, inheritance_start }
1418}
1419
1420pub(crate) fn base_override_functions(
1421    gcx: _,
1422    proxy: crate::typeck::override_checker::OverrideProxy
1423) -> &'gcx [crate::typeck::override_checker::OverrideProxy] {
1424    crate::typeck::override_checker::base_override_functions(gcx, proxy)
1425}
1426
1427/// Returns the resolved NatSpec doc comments for the given doc ID.
1428pub fn natspec_doc_comments(gcx: _, id: hir::DocId) -> &'gcx [hir::NatSpecItem] {
1429    crate::natspec::resolve_doc_comments(gcx, id)
1430}
1431
1432/// Resolves a contract name within a source's scope for NatSpec `@inheritdoc`.
1433pub(crate) fn natspec_contract_in_source(
1434    gcx: _,
1435    key: NatSpecContractKey
1436) -> Option<hir::ContractId> {
1437    let (name, source_id) = key;
1438    gcx.symbol_resolver.source_scopes[source_id]
1439        .resolve(solar_interface::Ident { name, span: Span::DUMMY })
1440        .and_then(|decls| {
1441            decls.iter().find_map(|decl| match decl.res {
1442                hir::Res::Item(hir::ItemId::Contract(id)) => Some(id),
1443                _ => None,
1444            })
1445        })
1446}
1447
1448/// Returns the ABI signature of the given item. Only accepts functions, errors, and events.
1449pub fn item_signature(gcx: _, id: hir::ItemId) -> &'gcx str {
1450    let name = gcx.item_name(id);
1451    let tys = gcx.item_parameter_types(id);
1452    let in_library =
1453        gcx.hir.item(id).contract().is_some_and(|c| gcx.hir.contract(c).kind.is_library());
1454    gcx.bump().alloc_str(&gcx.mk_abi_signature(name.as_str(), tys.iter().copied(), in_library))
1455}
1456
1457pub(crate) fn item_selector(gcx: _, id: hir::ItemId) -> B256 {
1458    keccak256(gcx.item_signature(id))
1459}
1460
1461/// Returns the type of the given builtin.
1462pub fn type_of_builtin(gcx: _, builtin: Builtin) -> Ty<'gcx> {
1463    builtin.ty_impl(gcx)
1464}
1465
1466fn type_of_using_directive_cached(gcx: _, key: UsingDirectiveKey) -> Option<Ty<'gcx>> {
1467    // HIR nodes are arena-allocated for the lifetime of `GlobalCtxt`.
1468    let using = unsafe { &*(key as *const hir::UsingDirective<'gcx>) };
1469    using.ty.as_ref().map(|ty| gcx.type_of_hir_ty(ty))
1470}
1471
1472/// Returns the type of the given item.
1473pub fn type_of_item(gcx: _, id: hir::ItemId) -> Ty<'gcx> {
1474    let kind = match id {
1475        hir::ItemId::Contract(id) => TyKind::Contract(id),
1476        hir::ItemId::Function(id) => {
1477            let f = gcx.hir.function(id);
1478            return gcx.mk_ty_fn(TyFn {
1479                kind: TyFnKind::Internal,
1480                parameters: gcx.mk_item_tys(f.parameters),
1481                returns: gcx.mk_item_tys(f.returns),
1482                state_mutability: fn_state_mutability(TyFnKind::Internal, f.state_mutability),
1483                function_id: Some(id),
1484                attached: false,
1485            });
1486        }
1487        hir::ItemId::Variable(id) => {
1488            let var = gcx.hir.variable(id);
1489            let ty = gcx.type_of_hir_ty(&var.ty);
1490            return var_type(gcx, var, ty);
1491        }
1492        hir::ItemId::Struct(id) => TyKind::Struct(id),
1493        hir::ItemId::Enum(id) => TyKind::Enum(id),
1494        hir::ItemId::Udvt(id) => {
1495            let udvt = gcx.hir.udvt(id);
1496            if udvt.ty.kind.is_elementary()
1497                && let ty = gcx.type_of_hir_ty(&udvt.ty)
1498                && ty.is_value_type()
1499            {
1500                TyKind::Udvt(ty, id)
1501            } else {
1502                let msg = "the underlying type of UDVTs must be an elementary value type";
1503                return gcx.mk_ty_err(gcx.dcx().emit_err(udvt.ty.span, msg));
1504            }
1505        }
1506        hir::ItemId::Error(id) => {
1507            TyKind::Error(gcx.mk_item_tys(gcx.hir.error(id).parameters), id)
1508        }
1509        hir::ItemId::Event(id) => {
1510            TyKind::Event(gcx.mk_item_tys(gcx.hir.event(id).parameters), id)
1511        }
1512    };
1513    gcx.mk_ty(kind)
1514}
1515
1516/// Returns the types of the fields of the given struct.
1517pub fn struct_field_types(gcx: _, id: hir::StructId) -> &'gcx [Ty<'gcx>] {
1518    gcx.mk_ty_iter(gcx.hir.strukt(id).fields.iter().map(|&f| gcx.type_of_item(f.into())))
1519}
1520
1521/// Returns the recursiveness of the given struct.
1522pub fn struct_recursiveness(gcx: _, id: hir::StructId) -> Recursiveness {
1523    use solar_data_structures::cycle::*;
1524
1525    let r = CycleDetector::detect(gcx, id, |gcx, cd, id| {
1526        let s = gcx.hir.strukt(id);
1527
1528        if cd.depth() >= 256 {
1529            let guar = gcx.dcx().emit_err(s.span, "struct is too deeply nested");
1530            return CycleDetectorResult::Break(Either::Left(guar));
1531        }
1532
1533        for &field_id in s.fields {
1534            let field = gcx.hir.variable(field_id);
1535            let mut check = |ty: &hir::Type<'_>, dynamic: bool| {
1536                if let hir::TypeKind::Custom(hir::ItemId::Struct(other)) = ty.kind {
1537                    match cd.run(other) {
1538                        CycleDetectorResult::Continue => {}
1539                        CycleDetectorResult::Cycle(_) if dynamic => {
1540                            return CycleDetectorResult::Break(Either::Right(()));
1541                        }
1542                        r => return r,
1543                    }
1544                }
1545                CycleDetectorResult::Continue
1546            };
1547            let mut dynamic = false;
1548            let mut ty = &field.ty;
1549            while let hir::TypeKind::Array(array) = ty.kind {
1550                if array.size.is_none() {
1551                    dynamic = true;
1552                }
1553                ty = &array.element;
1554            }
1555            cdr_try!(check(ty, dynamic));
1556            if let ControlFlow::Break(r) = field.ty.visit(&gcx.hir, &mut |ty| check(ty, true).to_controlflow()) {
1557                return r;
1558            }
1559        }
1560
1561        CycleDetectorResult::Continue
1562    });
1563    match r {
1564        CycleDetectorResult::Continue => Recursiveness::None,
1565        CycleDetectorResult::Break(Either::Left(guar)) => Recursiveness::Infinite(guar),
1566        CycleDetectorResult::Break(Either::Right(())) => Recursiveness::Recursive,
1567        CycleDetectorResult::Cycle(id) => Recursiveness::Infinite(
1568            gcx.dcx().emit_err(gcx.item_span(id), "recursive struct definition")
1569        ),
1570    }
1571}
1572
1573fn native_members(gcx: _, ty: Ty<'gcx>) -> members::MemberList<'gcx> {
1574    members::native_members(gcx, ty)
1575}
1576
1577fn contract_type_members_in_context(
1578    gcx: _,
1579    key: (hir::ContractId, hir::ContractId)
1580) -> members::MemberList<'gcx> {
1581    let (id, current_contract) = key;
1582    members::contract_type_members_in_context(gcx, id, current_contract)
1583}
1584
1585fn internal_function_members_in_context(
1586    gcx: _,
1587    key: (hir::FunctionId, hir::ContractId)
1588) -> members::MemberList<'gcx> {
1589    let (id, current_contract) = key;
1590    gcx.bump().alloc_vec(members::internal_function_members_in_context(gcx, id, current_contract))
1591}
1592}
1593
1594fn var_type<'gcx>(gcx: Gcx<'gcx>, var: &'gcx hir::Variable<'gcx>, ty: Ty<'gcx>) -> Ty<'gcx> {
1595    use hir::DataLocation::*;
1596
1597    // https://github.com/argotorg/solidity/blob/48d40d5eaf97c835cf55896a7a161eedc57c57f9/libsolidity/ast/AST.cpp#L820
1598    let mut has_reference_or_mapping_type_slot = None;
1599    let mut has_reference_or_mapping_type = || {
1600        *has_reference_or_mapping_type_slot
1601            .get_or_insert_with(|| ty.is_reference_type() || ty.has_mapping(gcx))
1602    };
1603
1604    let mut func_vis = None;
1605    let mut locs;
1606    let allowed: &[_] = if var.is_state_variable() {
1607        &[None, Some(Transient)]
1608    } else if !has_reference_or_mapping_type() || var.is_event_or_error_parameter() {
1609        &[None]
1610    } else if var.is_callable_or_catch_parameter() {
1611        locs = SmallVec::<[_; 3]>::new();
1612        locs.push(Some(Memory));
1613        let mut is_constructor_parameter = false;
1614        if let Some(hir::ItemId::Function(f)) = var.parent {
1615            let f = gcx.hir.function(f);
1616            is_constructor_parameter = f.kind.is_constructor();
1617            if !var.is_try_catch_parameter() && !is_constructor_parameter {
1618                func_vis = Some(f.visibility);
1619            }
1620            if is_constructor_parameter
1621                || f.visibility <= hir::Visibility::Internal
1622                || f.contract.is_some_and(|c| gcx.hir.contract(c).kind.is_library())
1623            {
1624                locs.push(Some(Storage));
1625            }
1626        }
1627        if !var.is_try_catch_parameter() && !is_constructor_parameter {
1628            locs.push(Some(Calldata));
1629        }
1630        &locs
1631    } else if var.is_local_variable() {
1632        &[Some(Memory), Some(Storage), Some(Calldata)]
1633    } else {
1634        &[None]
1635    };
1636
1637    let mut var_loc = var.data_location;
1638    if !allowed.contains(&var_loc) {
1639        if !ty.references_error() {
1640            let msg = if !has_reference_or_mapping_type() {
1641                "data location can only be specified for array, struct or mapping types".to_string()
1642            } else if let Some(var_loc) = var_loc {
1643                format!("invalid data location `{var_loc}`")
1644            } else {
1645                "expected data location".to_string()
1646            };
1647            let mut err = gcx.dcx().err(msg).span(var.span);
1648            if has_reference_or_mapping_type() {
1649                let note = format!(
1650                    "data location must be {expected} for {vis}{descr}{got}",
1651                    expected = or_list(
1652                        allowed.iter().map(|d| format!("`{}`", DataLocation::opt_to_str(*d)))
1653                    ),
1654                    vis = if let Some(vis) = func_vis { format!("{vis} ") } else { String::new() },
1655                    descr = var.description(),
1656                    got = if let Some(var_loc) = var_loc {
1657                        format!(", but got `{var_loc}`")
1658                    } else {
1659                        String::new()
1660                    },
1661                );
1662                err = err.note(note);
1663            }
1664            err.emit();
1665        }
1666        var_loc = allowed[0];
1667    }
1668
1669    let ty_loc = if var.is_event_or_error_parameter() || var.is_file_level_variable() {
1670        Memory
1671    } else if var.is_state_variable() {
1672        let mut_specified = var.mutability.is_some();
1673        match var_loc {
1674            None => {
1675                if mut_specified {
1676                    Memory
1677                } else {
1678                    Storage
1679                }
1680            }
1681            Some(Transient) => {
1682                if mut_specified {
1683                    let msg = "transient cannot be used as data location for constant or immutable variables";
1684                    gcx.dcx().emit_err(var.span, msg);
1685                }
1686                if var.initializer.is_some() {
1687                    let msg =
1688                        "initialization of transient storage state variables is not supported";
1689                    gcx.dcx().emit_err(var.span, msg);
1690                }
1691                Transient
1692            }
1693            Some(_) => unreachable!(),
1694        }
1695    } else if var.is_struct_member() {
1696        Storage
1697    } else {
1698        match var_loc {
1699            Some(loc @ (Memory | Storage | Calldata)) => loc,
1700            Some(Transient) => unimplemented!(),
1701            None => {
1702                debug_assert!(!has_reference_or_mapping_type(), "data location not properly set");
1703                Memory
1704            }
1705        }
1706    };
1707
1708    ty.with_loc_if_ref(gcx, ty_loc)
1709}
1710
1711/// True if referencing the item returns its type directly rather than wrapped in Type().
1712fn is_value_ns(id: hir::ItemId) -> bool {
1713    matches!(
1714        id,
1715        hir::ItemId::Function(_)
1716            | hir::ItemId::Variable(_)
1717            | hir::ItemId::Error(_)
1718            | hir::ItemId::Event(_)
1719    )
1720}
1721
1722/// `OnceMap::insert` but with `Copy` keys and values.
1723#[inline]
1724fn cache_insert<K, V>(map: &FxOnceMap<K, V>, key: K, make_val: impl FnOnce(&K) -> V) -> V
1725where
1726    K: Copy + Eq + Hash,
1727    V: Copy,
1728{
1729    map.map_insert(key, make_val, cache_insert_with_result)
1730}
1731
1732#[inline]
1733fn cache_insert_with_result<K, V: Copy>(_: &K, v: &V) -> V {
1734    *v
1735}
1736
1737#[cfg(false)]
1738fn log_cache_query(name: &str, key: &dyn fmt::Debug) -> tracing::span::EnteredSpan {
1739    let guard = trace_span!("query", %name, ?key).entered();
1740    trace!("entered");
1741    guard
1742}
1743
1744#[cfg(false)]
1745fn log_cache_query_result(result: &dyn fmt::Debug, hit: bool) {
1746    trace!(?result, hit);
1747}