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#[derive(Clone, Copy, Debug)]
54pub struct InterfaceFunction<'gcx> {
55 pub id: hir::FunctionId,
57 pub selector: Selector,
59 pub ty: Ty<'gcx>,
61}
62
63#[derive(Clone, Copy, Debug)]
67pub struct InterfaceFunctions<'gcx> {
68 pub functions: &'gcx [InterfaceFunction<'gcx>],
70 pub inheritance_start: usize,
72}
73
74#[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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
85pub struct ResolvedCallee {
86 pub res: hir::Res,
87 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
100pub enum ResolvedMember {
101 Res(hir::Res),
103 StructField { struct_id: hir::StructId, field_index: usize },
105 EnumVariant { enum_id: hir::EnumId, variant_index: usize },
107}
108
109#[derive(Clone, Copy, Debug)]
111pub struct MemberCompletion<'gcx> {
112 pub member: members::Member<'gcx>,
114 pub resolved: Option<ResolvedMember>,
116}
117
118pub type CallableParamNames = SmallVec<[Option<Symbol>; 8]>;
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123pub enum CallableParamSource {
124 Function {
126 id: hir::FunctionId,
128 skips_receiver: bool,
133 },
134 FunctionType(hir::VariableId),
136 Struct(hir::StructId),
138 Event(hir::EventId),
140 Error(hir::ErrorId),
142}
143
144#[derive(Clone, Copy, Debug)]
146pub struct CallableSignature<'gcx> {
147 pub parameters: &'gcx [Ty<'gcx>],
149 pub returns: &'gcx [Ty<'gcx>],
151 pub param_source: Option<CallableParamSource>,
153}
154
155impl<'gcx> TypeckResults<'gcx> {
156 #[inline]
158 pub fn type_of_expr(&self, id: hir::ExprId) -> Option<Ty<'gcx>> {
159 self.expr_types.get(&id).copied()
160 }
161
162 #[inline]
164 pub fn resolved_callee(&self, id: hir::ExprId) -> Option<ResolvedCallee> {
165 self.resolved_callees.get(&id).copied()
166 }
167
168 #[inline]
170 pub fn resolved_member(&self, id: hir::ExprId) -> Option<ResolvedMember> {
171 self.resolved_members.get(&id).copied()
172 }
173
174 #[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 #[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 #[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 pub fn all(&self) -> &'gcx [InterfaceFunction<'gcx>] {
202 self.functions
203 }
204
205 pub fn own(&self) -> &'gcx [InterfaceFunction<'gcx>] {
207 &self.functions[..self.inheritance_start]
208 }
209
210 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#[derive(Clone, Copy, Debug)]
237pub enum Recursiveness {
238 None,
240 Recursive,
242 Infinite(ErrorGuaranteed),
244}
245
246impl Recursiveness {
247 #[inline]
249 pub fn is_none(self) -> bool {
250 matches!(self, Self::None)
251 }
252
253 #[inline]
255 pub fn is_recursive(self) -> bool {
256 !self.is_none()
257 }
258}
259
260#[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#[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
344pub 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 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 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 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 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 #[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 #[inline]
534 pub fn resolved_callee(self, id: hir::ExprId) -> Option<ResolvedCallee> {
535 self.typeck_results.get()?.resolved_callee(id)
536 }
537
538 #[inline]
540 pub fn resolved_member(self, id: hir::ExprId) -> Option<ResolvedMember> {
541 self.typeck_results.get()?.resolved_member(id)
542 }
543
544 #[inline]
546 pub fn builtin_member(self, id: hir::ExprId) -> Option<Builtin> {
547 self.typeck_results.get()?.builtin_member(id)
548 }
549
550 #[inline]
552 pub fn builtin_callee(self, id: hir::ExprId) -> Option<Builtin> {
553 self.typeck_results.get()?.builtin_callee(id)
554 }
555
556 #[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 #[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(¶meters, 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 pub fn get_file(self, name: impl Into<FileName>) -> Option<Arc<SourceFile>> {
640 self.sess.source_map().get_file(name)
641 }
642
643 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 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 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 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 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 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 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 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 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 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 #[inline]
766 pub fn item_name_opt(self, id: impl Into<hir::ItemId>) -> Option<Ident> {
767 self.hir.item(id).name()
768 }
769
770 #[inline]
772 pub fn item_span(self, id: impl Into<hir::ItemId>) -> Span {
773 self.hir.item(id).span()
774 }
775
776 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 pub fn event_selector(self, id: hir::EventId) -> B256 {
792 self.item_selector(id.into())
793 }
794
795 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 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 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 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 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 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 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 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 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! {
1314pub 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
1330pub 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 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 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 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
1427pub fn natspec_doc_comments(gcx: _, id: hir::DocId) -> &'gcx [hir::NatSpecItem] {
1429 crate::natspec::resolve_doc_comments(gcx, id)
1430}
1431
1432pub(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
1448pub 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
1461pub 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 let using = unsafe { &*(key as *const hir::UsingDirective<'gcx>) };
1469 using.ty.as_ref().map(|ty| gcx.type_of_hir_ty(ty))
1470}
1471
1472pub 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
1516pub 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
1521pub 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 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
1711fn 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#[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}