cairo_lang_semantic/
corelib.rs

1use cairo_lang_defs::ids::{
2    EnumId, GenericTypeId, ImplDefId, ModuleId, ModuleItemId, NamedLanguageElementId,
3    TraitFunctionId, TraitId,
4};
5use cairo_lang_diagnostics::{Maybe, ToOption};
6use cairo_lang_filesystem::ids::CrateId;
7use cairo_lang_syntax::node::Terminal;
8use cairo_lang_syntax::node::ast::{self, BinaryOperator, UnaryOperator};
9use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
10use cairo_lang_utils::{
11    Intern, LookupIntern, OptionFrom, extract_matches, require, try_extract_matches,
12};
13use num_bigint::BigInt;
14use num_traits::{Num, Signed, ToPrimitive, Zero};
15use smol_str::SmolStr;
16
17use crate::db::SemanticGroup;
18use crate::diagnostic::SemanticDiagnosticKind;
19use crate::expr::compute::ComputationContext;
20use crate::expr::inference::Inference;
21use crate::items::constant::ConstValue;
22use crate::items::enm::SemanticEnumEx;
23use crate::items::functions::{GenericFunctionId, ImplGenericFunctionId};
24use crate::items::imp::ImplLongId;
25use crate::items::trt::{
26    ConcreteTraitGenericFunctionId, ConcreteTraitGenericFunctionLongId, ConcreteTraitId,
27};
28use crate::items::us::SemanticUseEx;
29use crate::resolve::ResolvedGenericItem;
30use crate::types::{ConcreteEnumLongId, ConcreteExternTypeLongId};
31use crate::{
32    ConcreteEnumId, ConcreteFunction, ConcreteImplLongId, ConcreteTypeId, ConcreteVariant, Expr,
33    ExprId, ExprTuple, FunctionId, FunctionLongId, GenericArgumentId, TypeId, TypeLongId, semantic,
34};
35
36pub fn core_module(db: &dyn SemanticGroup) -> ModuleId {
37    let core_crate = db.core_crate();
38    ModuleId::CrateRoot(core_crate)
39}
40
41/// Returns the submodule of `base_module`, named `submodule_name`, if exists.
42pub fn get_submodule(
43    db: &dyn SemanticGroup,
44    base_module: ModuleId,
45    submodule_name: &str,
46) -> Option<ModuleId> {
47    let submodules = db.module_submodules(base_module).ok()?;
48    let syntax_db = db.upcast();
49    for (submodule_id, submodule) in submodules.iter() {
50        if submodule.name(syntax_db).text(syntax_db) == submodule_name {
51            return Some(ModuleId::Submodule(*submodule_id));
52        }
53    }
54    None
55}
56
57/// Returns a submodule of the corelib named `submodule_name`.
58/// If no such submodule exists, panics.
59pub fn core_submodule(db: &dyn SemanticGroup, submodule_name: &str) -> ModuleId {
60    get_submodule(db, core_module(db), submodule_name)
61        .unwrap_or_else(|| panic!("`{submodule_name}` is not a core submodule."))
62}
63
64pub fn core_crate(db: &dyn SemanticGroup) -> CrateId {
65    CrateId::core(db)
66}
67
68pub fn core_felt252_ty(db: &dyn SemanticGroup) -> TypeId {
69    get_core_ty_by_name(db, "felt252".into(), vec![])
70}
71
72/// Returns the concrete type of a bounded int type with a given min and max.
73pub fn bounded_int_ty(db: &dyn SemanticGroup, min: BigInt, max: BigInt) -> TypeId {
74    let internal = core_submodule(db, "internal");
75    let bounded_int = get_submodule(db, internal, "bounded_int")
76        .expect("Could not find bounded_int submodule in corelib.");
77    let size_ty = db.core_felt252_ty();
78    let lower_id = ConstValue::Int(min, size_ty).intern(db);
79    let upper_id = ConstValue::Int(max, size_ty).intern(db);
80    try_get_ty_by_name(db, bounded_int, "BoundedInt".into(), vec![
81        GenericArgumentId::Constant(lower_id),
82        GenericArgumentId::Constant(upper_id),
83    ])
84    .expect("could not find")
85}
86
87pub fn core_nonzero_ty(db: &dyn SemanticGroup, inner_type: TypeId) -> TypeId {
88    get_ty_by_name(db, core_submodule(db, "zeroable"), "NonZero".into(), vec![
89        GenericArgumentId::Type(inner_type),
90    ])
91}
92
93pub fn core_result_ty(db: &dyn SemanticGroup, ok_type: TypeId, err_type: TypeId) -> TypeId {
94    get_ty_by_name(db, core_submodule(db, "result"), "Result".into(), vec![
95        GenericArgumentId::Type(ok_type),
96        GenericArgumentId::Type(err_type),
97    ])
98}
99
100pub fn core_option_ty(db: &dyn SemanticGroup, some_type: TypeId) -> TypeId {
101    get_ty_by_name(db, core_submodule(db, "option"), "Option".into(), vec![
102        GenericArgumentId::Type(some_type),
103    ])
104}
105
106pub fn core_box_ty(db: &dyn SemanticGroup, inner_type: TypeId) -> TypeId {
107    get_ty_by_name(db, core_submodule(db, "box"), "Box".into(), vec![GenericArgumentId::Type(
108        inner_type,
109    )])
110}
111
112pub fn core_array_felt252_ty(db: &dyn SemanticGroup) -> TypeId {
113    get_core_ty_by_name(db, "Array".into(), vec![GenericArgumentId::Type(db.core_felt252_ty())])
114}
115
116pub fn try_get_core_ty_by_name(
117    db: &dyn SemanticGroup,
118    name: SmolStr,
119    generic_args: Vec<GenericArgumentId>,
120) -> Result<TypeId, SemanticDiagnosticKind> {
121    try_get_ty_by_name(db, db.core_module(), name, generic_args)
122}
123
124pub fn try_get_ty_by_name(
125    db: &dyn SemanticGroup,
126    module: ModuleId,
127    name: SmolStr,
128    generic_args: Vec<GenericArgumentId>,
129) -> Result<TypeId, SemanticDiagnosticKind> {
130    // This should not fail if the corelib is present.
131    let module_item_id = db
132        .module_item_by_name(module, name.clone())
133        .map_err(|_| SemanticDiagnosticKind::UnknownType)?
134        .ok_or(SemanticDiagnosticKind::UnknownType)?;
135    let generic_type = match module_item_id {
136        ModuleItemId::Use(use_id) => {
137            db.use_resolved_item(use_id).to_option().and_then(|resolved_generic_item| {
138                try_extract_matches!(resolved_generic_item, ResolvedGenericItem::GenericType)
139            })
140        }
141        ModuleItemId::TypeAlias(module_type_alias_id) => {
142            let ty = db
143                .module_type_alias_resolved_type(module_type_alias_id)
144                .expect("Could not find type alias.");
145            assert!(
146                db.module_type_alias_generic_params(module_type_alias_id).unwrap().is_empty(),
147                "Cannot get type aliases with params from corelib."
148            );
149            return Ok(ty);
150        }
151        _ => GenericTypeId::option_from(module_item_id),
152    }
153    .unwrap_or_else(|| panic!("{name} is not a type."));
154
155    Ok(semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::new(
156        db,
157        generic_type,
158        generic_args,
159    ))
160    .intern(db))
161}
162
163pub fn get_core_ty_by_name(
164    db: &dyn SemanticGroup,
165    name: SmolStr,
166    generic_args: Vec<GenericArgumentId>,
167) -> TypeId {
168    try_get_core_ty_by_name(db, name, generic_args).unwrap()
169}
170
171pub fn get_ty_by_name(
172    db: &dyn SemanticGroup,
173    module: ModuleId,
174    name: SmolStr,
175    generic_args: Vec<GenericArgumentId>,
176) -> TypeId {
177    try_get_ty_by_name(db, module, name, generic_args).unwrap()
178}
179
180pub fn core_bool_ty(db: &dyn SemanticGroup) -> TypeId {
181    let core_module = db.core_module();
182    // This should not fail if the corelib is present.
183    let generic_type = db
184        .module_item_by_name(core_module, "bool".into())
185        .expect("Failed to load core lib.")
186        .and_then(GenericTypeId::option_from)
187        .expect("Type bool was not found in core lib.");
188    semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::new(db, generic_type, vec![]))
189        .intern(db)
190}
191
192// TODO(spapini): Consider making all these queries for better caching.
193/// Generates a ConcreteEnumId instance for `bool`.
194pub fn core_bool_enum(db: &dyn SemanticGroup) -> ConcreteEnumId {
195    let core_module = db.core_module();
196    // This should not fail if the corelib is present.
197    let enum_id = db
198        .module_item_by_name(core_module, "bool".into())
199        .expect("Failed to load core lib.")
200        .and_then(EnumId::option_from)
201        .expect("Type bool was not found in core lib.");
202    ConcreteEnumLongId { enum_id, generic_args: vec![] }.intern(db)
203}
204
205/// Generates a ConcreteVariant instance for `false`.
206pub fn false_variant(db: &dyn SemanticGroup) -> ConcreteVariant {
207    get_core_enum_concrete_variant(db, "bool", vec![], "False")
208}
209
210/// Generates a ConcreteVariant instance for `true`.
211pub fn true_variant(db: &dyn SemanticGroup) -> ConcreteVariant {
212    get_core_enum_concrete_variant(db, "bool", vec![], "True")
213}
214
215/// Generates a ConcreteVariant instance for `IsZeroResult::<felt252>::Zero`.
216pub fn jump_nz_zero_variant(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteVariant {
217    get_enum_concrete_variant(
218        db,
219        core_submodule(db, "zeroable"),
220        "IsZeroResult",
221        vec![GenericArgumentId::Type(ty)],
222        "Zero",
223    )
224}
225
226/// Generates a ConcreteVariant instance for `IsZeroResult::<felt252>::NonZero`.
227pub fn jump_nz_nonzero_variant(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteVariant {
228    get_enum_concrete_variant(
229        db,
230        core_submodule(db, "zeroable"),
231        "IsZeroResult",
232        vec![GenericArgumentId::Type(ty)],
233        "NonZero",
234    )
235}
236
237/// Generates a ConcreteVariant instance for `Option::Some`.
238pub fn option_some_variant(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteVariant {
239    get_enum_concrete_variant(
240        db,
241        core_submodule(db, "option"),
242        "Option",
243        vec![GenericArgumentId::Type(ty)],
244        "Some",
245    )
246}
247
248/// Generates a ConcreteVariant instance for `Option::None`.
249pub fn option_none_variant(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteVariant {
250    get_enum_concrete_variant(
251        db,
252        core_submodule(db, "option"),
253        "Option",
254        vec![GenericArgumentId::Type(ty)],
255        "None",
256    )
257}
258
259/// Gets a semantic expression of the literal `false`. Uses the given `stable_ptr` in the returned
260/// semantic expression.
261pub fn false_literal_expr(
262    ctx: &mut ComputationContext<'_>,
263    stable_ptr: ast::ExprPtr,
264) -> semantic::Expr {
265    get_bool_variant_expr(ctx, "bool", "False", stable_ptr)
266}
267
268/// Gets a semantic expression of the literal `true`. Uses the given `stable_ptr` in the returned
269/// semantic expression.
270pub fn true_literal_expr(
271    ctx: &mut ComputationContext<'_>,
272    stable_ptr: ast::ExprPtr,
273) -> semantic::Expr {
274    get_bool_variant_expr(ctx, "bool", "True", stable_ptr)
275}
276
277/// Gets a semantic expression of the specified bool enum variant. Uses the given `stable_ptr` in
278/// the returned semantic expression.
279fn get_bool_variant_expr(
280    ctx: &mut ComputationContext<'_>,
281    enum_name: &str,
282    variant_name: &str,
283    stable_ptr: ast::ExprPtr,
284) -> semantic::Expr {
285    let concrete_variant = get_core_enum_concrete_variant(ctx.db, enum_name, vec![], variant_name);
286    semantic::Expr::EnumVariantCtor(semantic::ExprEnumVariantCtor {
287        variant: concrete_variant,
288        value_expr: unit_expr(ctx, stable_ptr),
289        ty: core_bool_ty(ctx.db),
290        stable_ptr,
291    })
292}
293
294/// Gets a [ConcreteVariant] instance for an enum variant, by module and name.
295/// Assumes the variant exists.
296pub fn get_enum_concrete_variant(
297    db: &dyn SemanticGroup,
298    module_id: ModuleId,
299    enum_name: &str,
300    generic_args: Vec<GenericArgumentId>,
301    variant_name: &str,
302) -> ConcreteVariant {
303    let ty = get_ty_by_name(db, module_id, enum_name.into(), generic_args);
304    let concrete_ty = extract_matches!(ty.lookup_intern(db), TypeLongId::Concrete);
305    let concrete_enum_id = extract_matches!(concrete_ty, ConcreteTypeId::Enum);
306    let enum_id = concrete_enum_id.enum_id(db);
307    let variant_id = db.enum_variants(enum_id).unwrap()[variant_name];
308    let variant = db.variant_semantic(enum_id, variant_id).unwrap();
309    db.concrete_enum_variant(concrete_enum_id, &variant).unwrap()
310}
311
312/// Gets a [ConcreteVariant] instance for an enum variant from the core module, by name.
313/// Assumes the variant exists.
314pub fn get_core_enum_concrete_variant(
315    db: &dyn SemanticGroup,
316    enum_name: &str,
317    generic_args: Vec<GenericArgumentId>,
318    variant_name: &str,
319) -> ConcreteVariant {
320    get_enum_concrete_variant(db, core_module(db), enum_name, generic_args, variant_name)
321}
322
323/// Gets the unit type ().
324pub fn unit_ty(db: &dyn SemanticGroup) -> TypeId {
325    semantic::TypeLongId::Tuple(vec![]).intern(db)
326}
327
328/// Gets the never type ().
329pub fn never_ty(db: &dyn SemanticGroup) -> TypeId {
330    let core_module = db.core_module();
331    // This should not fail if the corelib is present.
332    let generic_type = db
333        .module_item_by_name(core_module, "never".into())
334        .expect("Failed to load core lib.")
335        .and_then(GenericTypeId::option_from)
336        .expect("Type bool was not found in core lib.");
337    semantic::TypeLongId::Concrete(semantic::ConcreteTypeId::new(db, generic_type, vec![]))
338        .intern(db)
339}
340
341pub enum ErrorPropagationType {
342    Option { some_variant: ConcreteVariant, none_variant: ConcreteVariant },
343    Result { ok_variant: ConcreteVariant, err_variant: ConcreteVariant },
344}
345impl ErrorPropagationType {
346    pub fn ok_variant(&self) -> &ConcreteVariant {
347        match self {
348            ErrorPropagationType::Option { some_variant, .. } => some_variant,
349            ErrorPropagationType::Result { ok_variant, .. } => ok_variant,
350        }
351    }
352    pub fn err_variant(&self) -> &ConcreteVariant {
353        match self {
354            ErrorPropagationType::Option { none_variant, .. } => none_variant,
355            ErrorPropagationType::Result { err_variant, .. } => err_variant,
356        }
357    }
358}
359
360/// Attempts to unwrap error propagation types (Option, Result).
361/// Returns None if not one of these types.
362pub fn unwrap_error_propagation_type(
363    db: &dyn SemanticGroup,
364    ty: TypeId,
365) -> Option<ErrorPropagationType> {
366    match ty.lookup_intern(db) {
367        // Only enums may be `Result` and `Option` types.
368        TypeLongId::Concrete(semantic::ConcreteTypeId::Enum(enm)) => {
369            if let [ok_variant, err_variant] =
370                db.concrete_enum_variants(enm).to_option()?.as_slice()
371            {
372                let name = enm.enum_id(db.upcast()).name(db.upcast());
373                if name == "Option" {
374                    return Some(ErrorPropagationType::Option {
375                        some_variant: ok_variant.clone(),
376                        none_variant: err_variant.clone(),
377                    });
378                } else if name == "Result" {
379                    return Some(ErrorPropagationType::Result {
380                        ok_variant: ok_variant.clone(),
381                        err_variant: err_variant.clone(),
382                    });
383                }
384            }
385            None
386        }
387        TypeLongId::GenericParameter(_) => todo!(
388            "When generic types are supported, if type is of matching type, allow unwrapping it \
389             to type."
390        ),
391        TypeLongId::Concrete(
392            semantic::ConcreteTypeId::Struct(_) | semantic::ConcreteTypeId::Extern(_),
393        )
394        | TypeLongId::Tuple(_)
395        | TypeLongId::Snapshot(_)
396        | TypeLongId::Var(_)
397        | TypeLongId::Coupon(_)
398        | TypeLongId::ImplType(_)
399        | TypeLongId::Missing(_)
400        | TypeLongId::FixedSizeArray { .. }
401        | TypeLongId::Closure(_) => None,
402        // TODO(yuval): for trait function default implementation, this may need to change.
403        TypeLongId::TraitType(_) => {
404            panic!("Trait types should only appear in traits, where there are no function bodies.")
405        }
406    }
407}
408
409/// builds a semantic unit expression. This is not necessarily located in the AST, so it is received
410/// as a param.
411pub fn unit_expr(ctx: &mut ComputationContext<'_>, stable_ptr: ast::ExprPtr) -> ExprId {
412    ctx.arenas.exprs.alloc(Expr::Tuple(ExprTuple {
413        items: Vec::new(),
414        ty: TypeLongId::Tuple(Vec::new()).intern(ctx.db),
415        stable_ptr,
416    }))
417}
418
419pub fn core_unary_operator(
420    db: &dyn SemanticGroup,
421    inference: &mut Inference<'_>,
422    unary_op: &UnaryOperator,
423    stable_ptr: SyntaxStablePtrId,
424) -> Maybe<Result<ConcreteTraitGenericFunctionId, SemanticDiagnosticKind>> {
425    let (trait_name, function_name) = match unary_op {
426        UnaryOperator::Minus(_) => ("Neg", "neg"),
427        UnaryOperator::Not(_) => ("Not", "not"),
428        UnaryOperator::BitNot(_) => ("BitNot", "bitnot"),
429        UnaryOperator::At(_) => unreachable!("@ is not an unary operator."),
430        UnaryOperator::Desnap(_) => unreachable!("* is not an unary operator."),
431    };
432    Ok(Ok(get_core_trait_function_infer(
433        db,
434        inference,
435        CoreTraitContext::TopLevel,
436        trait_name.into(),
437        function_name.into(),
438        stable_ptr,
439    )))
440}
441
442pub fn core_binary_operator(
443    db: &dyn SemanticGroup,
444    inference: &mut Inference<'_>,
445    binary_op: &BinaryOperator,
446    stable_ptr: SyntaxStablePtrId,
447) -> Maybe<Result<(ConcreteTraitGenericFunctionId, bool), SemanticDiagnosticKind>> {
448    let (trait_name, function_name, snapshot, context) = match binary_op {
449        BinaryOperator::Plus(_) => ("Add", "add", false, CoreTraitContext::TopLevel),
450        BinaryOperator::PlusEq(_) => ("AddAssign", "add_assign", false, CoreTraitContext::Ops),
451        BinaryOperator::Minus(_) => ("Sub", "sub", false, CoreTraitContext::TopLevel),
452        BinaryOperator::MinusEq(_) => ("SubAssign", "sub_assign", false, CoreTraitContext::Ops),
453        BinaryOperator::Mul(_) => ("Mul", "mul", false, CoreTraitContext::TopLevel),
454        BinaryOperator::MulEq(_) => ("MulAssign", "mul_assign", false, CoreTraitContext::Ops),
455        BinaryOperator::Div(_) => ("Div", "div", false, CoreTraitContext::TopLevel),
456        BinaryOperator::DivEq(_) => ("DivAssign", "div_assign", false, CoreTraitContext::Ops),
457        BinaryOperator::Mod(_) => ("Rem", "rem", false, CoreTraitContext::TopLevel),
458        BinaryOperator::ModEq(_) => ("RemAssign", "rem_assign", false, CoreTraitContext::Ops),
459        BinaryOperator::EqEq(_) => ("PartialEq", "eq", true, CoreTraitContext::TopLevel),
460        BinaryOperator::Neq(_) => ("PartialEq", "ne", true, CoreTraitContext::TopLevel),
461        BinaryOperator::LE(_) => ("PartialOrd", "le", false, CoreTraitContext::TopLevel),
462        BinaryOperator::GE(_) => ("PartialOrd", "ge", false, CoreTraitContext::TopLevel),
463        BinaryOperator::LT(_) => ("PartialOrd", "lt", false, CoreTraitContext::TopLevel),
464        BinaryOperator::GT(_) => ("PartialOrd", "gt", false, CoreTraitContext::TopLevel),
465        BinaryOperator::And(_) => ("BitAnd", "bitand", false, CoreTraitContext::TopLevel),
466        BinaryOperator::Or(_) => ("BitOr", "bitor", false, CoreTraitContext::TopLevel),
467        BinaryOperator::Xor(_) => ("BitXor", "bitxor", false, CoreTraitContext::TopLevel),
468        BinaryOperator::DotDot(_) => ("RangeOp", "range", false, CoreTraitContext::Ops),
469        _ => return Ok(Err(SemanticDiagnosticKind::UnknownBinaryOperator)),
470    };
471    Ok(Ok((
472        get_core_trait_function_infer(
473            db,
474            inference,
475            context,
476            trait_name.into(),
477            function_name.into(),
478            stable_ptr,
479        ),
480        snapshot,
481    )))
482}
483
484pub fn felt252_eq(db: &dyn SemanticGroup) -> FunctionId {
485    get_core_function_impl_method(db, "Felt252PartialEq".into(), "eq".into())
486}
487
488pub fn felt252_sub(db: &dyn SemanticGroup) -> FunctionId {
489    get_core_function_impl_method(db, "Felt252Sub".into(), "sub".into())
490}
491
492/// Given a core library impl name and a method name, returns [FunctionId].
493fn get_core_function_impl_method(
494    db: &dyn SemanticGroup,
495    impl_name: SmolStr,
496    method_name: SmolStr,
497) -> FunctionId {
498    let core_module = db.core_module();
499    let module_item_id = db
500        .module_item_by_name(core_module, impl_name.clone())
501        .expect("Failed to load core lib.")
502        .unwrap_or_else(|| panic!("Impl '{impl_name}' was not found in core lib."));
503    let impl_def_id = match module_item_id {
504        ModuleItemId::Use(use_id) => {
505            db.use_resolved_item(use_id).to_option().and_then(|resolved_generic_item| {
506                try_extract_matches!(resolved_generic_item, ResolvedGenericItem::Impl)
507            })
508        }
509        _ => ImplDefId::option_from(module_item_id),
510    }
511    .unwrap_or_else(|| panic!("{impl_name} is not an impl."));
512    let impl_id =
513        ImplLongId::Concrete(ConcreteImplLongId { impl_def_id, generic_args: vec![] }.intern(db))
514            .intern(db);
515    let concrete_trait_id = db.impl_concrete_trait(impl_id).unwrap();
516    let function = db
517        .trait_functions(concrete_trait_id.trait_id(db))
518        .ok()
519        .and_then(|functions| functions.get(&method_name).cloned())
520        .unwrap_or_else(|| {
521            panic!("no {method_name} in {}.", concrete_trait_id.trait_id(db).name(db.upcast()))
522        });
523    FunctionLongId {
524        function: ConcreteFunction {
525            generic_function: GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function }),
526            generic_args: vec![],
527        },
528    }
529    .intern(db)
530}
531
532pub fn core_felt252_is_zero(db: &dyn SemanticGroup) -> FunctionId {
533    get_core_function_id(db, "felt252_is_zero".into(), vec![])
534}
535
536/// The gas withdrawal functions from the `gas` submodule.
537pub fn core_withdraw_gas_fns(db: &dyn SemanticGroup) -> [FunctionId; 2] {
538    let gas = core_submodule(db, "gas");
539    [
540        get_function_id(db, gas, "withdraw_gas".into(), vec![]),
541        get_function_id(db, gas, "withdraw_gas_all".into(), vec![]),
542    ]
543}
544
545pub fn internal_require_implicit(db: &dyn SemanticGroup) -> GenericFunctionId {
546    get_generic_function_id(db, core_submodule(db, "internal"), "require_implicit".into())
547}
548/// The function `downcast` from the `integer` submodule.
549pub fn core_downcast(db: &dyn SemanticGroup, input: TypeId, output: TypeId) -> FunctionId {
550    let internal = core_submodule(db, "integer");
551
552    get_function_id(db, internal, "downcast".into(), vec![
553        GenericArgumentId::Type(input),
554        GenericArgumentId::Type(output),
555    ])
556}
557/// Given a core library function name and its generic arguments, returns [FunctionId].
558pub fn get_core_function_id(
559    db: &dyn SemanticGroup,
560    name: SmolStr,
561    generic_args: Vec<GenericArgumentId>,
562) -> FunctionId {
563    get_function_id(db, db.core_module(), name, generic_args)
564}
565
566/// Given a module, a library function name and its generic arguments, returns [FunctionId].
567pub fn get_function_id(
568    db: &dyn SemanticGroup,
569    module: ModuleId,
570    name: SmolStr,
571    generic_args: Vec<GenericArgumentId>,
572) -> FunctionId {
573    let generic_function = get_generic_function_id(db, module, name);
574
575    FunctionLongId { function: ConcreteFunction { generic_function, generic_args } }.intern(db)
576}
577
578/// Given a core library function name, returns [GenericFunctionId].
579pub fn get_core_generic_function_id(db: &dyn SemanticGroup, name: SmolStr) -> GenericFunctionId {
580    get_generic_function_id(db, db.core_module(), name)
581}
582
583/// Given a module and a library function name, returns [GenericFunctionId].
584pub fn get_generic_function_id(
585    db: &dyn SemanticGroup,
586    module: ModuleId,
587    name: SmolStr,
588) -> GenericFunctionId {
589    let module_item_id = db
590        .module_item_by_name(module, name.clone())
591        .expect("Failed to load core lib.")
592        .unwrap_or_else(|| panic!("Function '{name}' was not found in core lib."));
593    match module_item_id {
594        ModuleItemId::Use(use_id) => {
595            db.use_resolved_item(use_id).to_option().and_then(|resolved_generic_item| {
596                try_extract_matches!(resolved_generic_item, ResolvedGenericItem::GenericFunction)
597            })
598        }
599        _ => GenericFunctionId::option_from(module_item_id),
600    }
601    .unwrap_or_else(|| panic!("{name} is not a function."))
602}
603
604pub fn concrete_copy_trait(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteTraitId {
605    get_core_concrete_trait(db, "Copy".into(), vec![GenericArgumentId::Type(ty)])
606}
607
608pub fn concrete_drop_trait(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteTraitId {
609    get_core_concrete_trait(db, "Drop".into(), vec![GenericArgumentId::Type(ty)])
610}
611
612pub fn concrete_destruct_trait(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteTraitId {
613    get_core_concrete_trait(db, "Destruct".into(), vec![GenericArgumentId::Type(ty)])
614}
615
616pub fn concrete_panic_destruct_trait(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteTraitId {
617    get_core_concrete_trait(db, "PanicDestruct".into(), vec![GenericArgumentId::Type(ty)])
618}
619
620pub fn concrete_iterator_trait(db: &dyn SemanticGroup, ty: TypeId) -> ConcreteTraitId {
621    let trait_id = get_core_trait(db, CoreTraitContext::Iterator, "Iterator".into());
622    semantic::ConcreteTraitLongId { trait_id, generic_args: vec![GenericArgumentId::Type(ty)] }
623        .intern(db)
624}
625
626pub fn fn_once_trait(db: &dyn SemanticGroup) -> TraitId {
627    get_core_trait(db, CoreTraitContext::Ops, "FnOnce".into())
628}
629
630pub fn fn_trait(db: &dyn SemanticGroup) -> TraitId {
631    get_core_trait(db, CoreTraitContext::Ops, "Fn".into())
632}
633
634pub fn fn_traits(db: &dyn SemanticGroup) -> [TraitId; 2] {
635    [fn_trait(db), fn_once_trait(db)]
636}
637
638pub fn fn_once_call_trait_fn(db: &dyn SemanticGroup) -> TraitFunctionId {
639    get_core_trait_fn(db, CoreTraitContext::Ops, "FnOnce".into(), "call".into())
640}
641
642pub fn fn_call_trait_fn(db: &dyn SemanticGroup) -> TraitFunctionId {
643    get_core_trait_fn(db, CoreTraitContext::Ops, "Fn".into(), "call".into())
644}
645
646pub fn copy_trait(db: &dyn SemanticGroup) -> TraitId {
647    get_core_trait(db, CoreTraitContext::TopLevel, "Copy".into())
648}
649
650pub fn drop_trait(db: &dyn SemanticGroup) -> TraitId {
651    get_core_trait(db, CoreTraitContext::TopLevel, "Drop".into())
652}
653
654pub fn destruct_trait(db: &dyn SemanticGroup) -> TraitId {
655    get_core_trait(db, CoreTraitContext::TopLevel, "Destruct".into())
656}
657
658pub fn panic_destruct_trait(db: &dyn SemanticGroup) -> TraitId {
659    get_core_trait(db, CoreTraitContext::TopLevel, "PanicDestruct".into())
660}
661
662pub fn deref_trait(db: &dyn SemanticGroup) -> TraitId {
663    get_core_trait(db, CoreTraitContext::Ops, "Deref".into())
664}
665
666pub fn deref_mut_trait(db: &dyn SemanticGroup) -> TraitId {
667    get_core_trait(db, CoreTraitContext::Ops, "DerefMut".into())
668}
669
670pub fn destruct_trait_fn(db: &dyn SemanticGroup) -> TraitFunctionId {
671    get_core_trait_fn(db, CoreTraitContext::TopLevel, "Destruct".into(), "destruct".into())
672}
673
674pub fn panic_destruct_trait_fn(db: &dyn SemanticGroup) -> TraitFunctionId {
675    get_core_trait_fn(
676        db,
677        CoreTraitContext::TopLevel,
678        "PanicDestruct".into(),
679        "panic_destruct".into(),
680    )
681}
682
683pub fn into_iterator_trait(db: &dyn SemanticGroup) -> TraitId {
684    get_core_trait(db, CoreTraitContext::Iterator, "IntoIterator".into())
685}
686
687pub fn numeric_literal_trait(db: &dyn SemanticGroup) -> TraitId {
688    get_core_trait(db, CoreTraitContext::TopLevel, "NumericLiteral".into())
689}
690
691/// Given a core library trait name and its generic arguments, returns [ConcreteTraitId].
692fn get_core_concrete_trait(
693    db: &dyn SemanticGroup,
694    name: SmolStr,
695    generic_args: Vec<GenericArgumentId>,
696) -> ConcreteTraitId {
697    let trait_id = get_core_trait(db, CoreTraitContext::TopLevel, name);
698    semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(db)
699}
700
701/// The context for a core library trait.
702pub enum CoreTraitContext {
703    /// The top level core library context.
704    TopLevel,
705    /// The ops core library context.
706    Ops,
707    /// The iterator core library context.
708    Iterator,
709    /// The meta programming core library context.
710    MetaProgramming,
711}
712
713/// Given a core library context and trait name, returns [TraitId].
714pub fn get_core_trait(db: &dyn SemanticGroup, context: CoreTraitContext, name: SmolStr) -> TraitId {
715    let base_module = match context {
716        CoreTraitContext::TopLevel => db.core_module(),
717        CoreTraitContext::Ops => core_submodule(db, "ops"),
718        CoreTraitContext::Iterator => core_submodule(db, "iter"),
719        CoreTraitContext::MetaProgramming => core_submodule(db, "metaprogramming"),
720    };
721    // This should not fail if the corelib is present.
722    let item_id = db
723        .module_item_by_name(base_module, name.clone())
724        .unwrap_or_else(|_| {
725            panic!(
726                "Core module `{module}` failed to compile.",
727                module = base_module.full_path(db.upcast())
728            )
729        })
730        .unwrap_or_else(|| {
731            panic!(
732                "Core module `{module}` is missing an use item for trait `{name}`.",
733                module = base_module.full_path(db.upcast()),
734            )
735        });
736    match item_id {
737        ModuleItemId::Trait(id) => id,
738        ModuleItemId::Use(use_id) => {
739            extract_matches!(
740                db.use_resolved_item(use_id).unwrap_or_else(|_| panic!(
741                    "Could not resolve core trait `{module}::{name}`.",
742                    module = base_module.full_path(db.upcast()),
743                )),
744                ResolvedGenericItem::Trait
745            )
746        }
747        _ => panic!("Expecting only traits, or uses pointing to traits."),
748    }
749}
750
751/// Given a core library context, trait name and fn name, returns [TraitFunctionId].
752fn get_core_trait_fn(
753    db: &dyn SemanticGroup,
754    context: CoreTraitContext,
755    trait_name: SmolStr,
756    fn_name: SmolStr,
757) -> TraitFunctionId {
758    db.trait_function_by_name(get_core_trait(db, context, trait_name), fn_name).unwrap().unwrap()
759}
760
761/// Retrieves a trait function from the core library with type variables as generic arguments, to
762/// be inferred later.
763fn get_core_trait_function_infer(
764    db: &dyn SemanticGroup,
765    inference: &mut Inference<'_>,
766    context: CoreTraitContext,
767    trait_name: SmolStr,
768    function_name: SmolStr,
769    stable_ptr: SyntaxStablePtrId,
770) -> ConcreteTraitGenericFunctionId {
771    let trait_id = get_core_trait(db, context, trait_name.clone());
772    let generic_params = db.trait_generic_params(trait_id).unwrap();
773    let generic_args = generic_params
774        .iter()
775        .map(|_| GenericArgumentId::Type(inference.new_type_var(Some(stable_ptr))))
776        .collect();
777    let concrete_trait_id = semantic::ConcreteTraitLongId { trait_id, generic_args }.intern(db);
778    let trait_function = db
779        .trait_function_by_name(trait_id, function_name.clone())
780        .unwrap()
781        .unwrap_or_else(move || panic!("Missing function '{function_name}' in '{trait_name}'."));
782    ConcreteTraitGenericFunctionLongId::new(db, concrete_trait_id, trait_function).intern(db)
783}
784
785pub fn get_panic_ty(db: &dyn SemanticGroup, inner_ty: TypeId) -> TypeId {
786    get_core_ty_by_name(db.upcast(), "PanicResult".into(), vec![GenericArgumentId::Type(inner_ty)])
787}
788
789pub fn get_usize_ty(db: &dyn SemanticGroup) -> TypeId {
790    get_core_ty_by_name(db, "usize".into(), vec![])
791}
792
793/// Returns [FunctionId] of the libfunc that converts type of `ty` to felt252.
794pub fn get_convert_to_felt252_libfunc_name_by_type(
795    db: &dyn SemanticGroup,
796    ty: TypeId,
797) -> Option<FunctionId> {
798    if ty == get_core_ty_by_name(db, "u8".into(), vec![]) {
799        Some(get_function_id(db, core_submodule(db, "integer"), "u8_to_felt252".into(), vec![]))
800    } else if ty == get_core_ty_by_name(db, "u16".into(), vec![]) {
801        Some(get_function_id(db, core_submodule(db, "integer"), "u16_to_felt252".into(), vec![]))
802    } else if ty == get_core_ty_by_name(db, "u32".into(), vec![]) {
803        Some(get_function_id(db, core_submodule(db, "integer"), "u32_to_felt252".into(), vec![]))
804    } else if ty == get_core_ty_by_name(db, "u64".into(), vec![]) {
805        Some(get_function_id(db, core_submodule(db, "integer"), "u64_to_felt252".into(), vec![]))
806    } else if ty == get_core_ty_by_name(db, "u128".into(), vec![]) {
807        Some(get_function_id(db, core_submodule(db, "integer"), "u128_to_felt252".into(), vec![]))
808    } else if ty == get_core_ty_by_name(db, "i8".into(), vec![]) {
809        Some(get_function_id(db, core_submodule(db, "integer"), "i8_to_felt252".into(), vec![]))
810    } else if ty == get_core_ty_by_name(db, "i16".into(), vec![]) {
811        Some(get_function_id(db, core_submodule(db, "integer"), "i16_to_felt252".into(), vec![]))
812    } else if ty == get_core_ty_by_name(db, "i32".into(), vec![]) {
813        Some(get_function_id(db, core_submodule(db, "integer"), "i32_to_felt252".into(), vec![]))
814    } else if ty == get_core_ty_by_name(db, "i64".into(), vec![]) {
815        Some(get_function_id(db, core_submodule(db, "integer"), "i64_to_felt252".into(), vec![]))
816    } else if ty == get_core_ty_by_name(db, "i128".into(), vec![]) {
817        Some(get_function_id(db, core_submodule(db, "integer"), "i128_to_felt252".into(), vec![]))
818    } else {
819        None
820    }
821}
822
823#[derive(Clone, Debug, Eq, Hash, PartialEq)]
824pub enum LiteralError {
825    InvalidTypeForLiteral(TypeId),
826    OutOfRange(TypeId),
827}
828impl LiteralError {
829    pub fn format(&self, db: &dyn SemanticGroup) -> String {
830        match self {
831            Self::OutOfRange(ty) => format!(
832                "The value does not fit within the range of type {}.",
833                ty.format(db.upcast())
834            ),
835            Self::InvalidTypeForLiteral(ty) => {
836                format!("A numeric literal of type {} cannot be created.", ty.format(db.upcast()))
837            }
838        }
839    }
840}
841
842/// Validates that a given type is valid for a literal and that the value fits the range of the
843/// specific type.
844pub fn validate_literal(
845    db: &dyn SemanticGroup,
846    ty: TypeId,
847    value: BigInt,
848) -> Result<(), LiteralError> {
849    if let Some(nz_wrapped_ty) = try_extract_nz_wrapped_type(db, ty) {
850        return if value.is_zero() {
851            Err(LiteralError::OutOfRange(ty))
852        } else {
853            validate_literal(db, nz_wrapped_ty, value)
854        };
855    }
856    let is_out_of_range = if let Some((min, max)) = try_extract_bounded_int_type_ranges(db, ty) {
857        value < min || value > max
858    } else if ty == db.core_felt252_ty() {
859        value.abs()
860            > BigInt::from_str_radix(
861                "800000000000011000000000000000000000000000000000000000000000000",
862                16,
863            )
864            .unwrap()
865    } else if ty == get_core_ty_by_name(db, "u8".into(), vec![]) {
866        value.to_u8().is_none()
867    } else if ty == get_core_ty_by_name(db, "u16".into(), vec![]) {
868        value.to_u16().is_none()
869    } else if ty == get_core_ty_by_name(db, "u32".into(), vec![]) {
870        value.to_u32().is_none()
871    } else if ty == get_core_ty_by_name(db, "u64".into(), vec![]) {
872        value.to_u64().is_none()
873    } else if ty == get_core_ty_by_name(db, "u128".into(), vec![]) {
874        value.to_u128().is_none()
875    } else if ty == get_core_ty_by_name(db, "u256".into(), vec![]) {
876        value.is_negative() || value.bits() > 256
877    } else if ty == get_core_ty_by_name(db, "i8".into(), vec![]) {
878        value.to_i8().is_none()
879    } else if ty == get_core_ty_by_name(db, "i16".into(), vec![]) {
880        value.to_i16().is_none()
881    } else if ty == get_core_ty_by_name(db, "i32".into(), vec![]) {
882        value.to_i32().is_none()
883    } else if ty == get_core_ty_by_name(db, "i64".into(), vec![]) {
884        value.to_i64().is_none()
885    } else if ty == get_core_ty_by_name(db, "i128".into(), vec![]) {
886        value.to_i128().is_none()
887    } else {
888        return Err(LiteralError::InvalidTypeForLiteral(ty));
889    };
890    if is_out_of_range { Err(LiteralError::OutOfRange(ty)) } else { Ok(()) }
891}
892
893/// Returns the type if the inner value of a `NonZero` type, if it is wrapped in one.
894pub fn try_extract_nz_wrapped_type(db: &dyn SemanticGroup, ty: TypeId) -> Option<TypeId> {
895    let concrete_ty = try_extract_matches!(ty.lookup_intern(db), TypeLongId::Concrete)?;
896    let extern_ty = try_extract_matches!(concrete_ty, ConcreteTypeId::Extern)?;
897    let ConcreteExternTypeLongId { extern_type_id, generic_args } = extern_ty.lookup_intern(db);
898    let [GenericArgumentId::Type(inner)] = generic_args[..] else { return None };
899    (extern_type_id.name(db.upcast()) == "NonZero").then_some(inner)
900}
901
902/// Returns the ranges of a BoundedInt if it is a BoundedInt type.
903fn try_extract_bounded_int_type_ranges(
904    db: &dyn SemanticGroup,
905    ty: TypeId,
906) -> Option<(BigInt, BigInt)> {
907    let concrete_ty = try_extract_matches!(db.lookup_intern_type(ty), TypeLongId::Concrete)?;
908    let extern_ty = try_extract_matches!(concrete_ty, ConcreteTypeId::Extern)?;
909    let ConcreteExternTypeLongId { extern_type_id, generic_args } =
910        db.lookup_intern_concrete_extern_type(extern_ty);
911    require(extern_type_id.name(db.upcast()) == "BoundedInt")?;
912    let [GenericArgumentId::Constant(min), GenericArgumentId::Constant(max)] = generic_args[..]
913    else {
914        return None;
915    };
916    let to_int = |id| db.lookup_intern_const_value(id).into_int();
917
918    Some((to_int(min)?, to_int(max)?))
919}