sway_core/language/ty/declaration/
function.rs

1use crate::{
2    decl_engine::*,
3    engine_threading::*,
4    has_changes,
5    language::{
6        parsed::{self, FunctionDeclaration, FunctionDeclarationKind},
7        ty::*,
8        CallPath, Inline, Purity, Visibility,
9    },
10    semantic_analysis::TypeCheckContext,
11    transform::{self, AttributeKind},
12    type_system::*,
13    types::*,
14};
15use monomorphization::MonomorphizeHelper;
16use serde::{Deserialize, Serialize};
17use sha2::{Digest, Sha256};
18use std::{
19    collections::BTreeMap,
20    fmt,
21    hash::{Hash, Hasher},
22};
23use sway_error::handler::{ErrorEmitted, Handler};
24use sway_types::{Ident, Named, Span, Spanned};
25
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub enum TyFunctionDeclKind {
28    Default,
29    Entry,
30    Main,
31    Test,
32}
33
34#[derive(Clone, Debug, Serialize, Deserialize)]
35pub struct TyFunctionDecl {
36    pub name: Ident,
37    pub body: TyCodeBlock,
38    pub parameters: Vec<TyFunctionParameter>,
39    pub implementing_type: Option<TyDecl>,
40    pub implementing_for_typeid: Option<TypeId>,
41    pub span: Span,
42    pub call_path: CallPath,
43    pub attributes: transform::Attributes,
44    pub type_parameters: Vec<TypeParameter>,
45    pub return_type: GenericArgument,
46    pub visibility: Visibility,
47    /// whether this function exists in another contract and requires a call to it or not
48    pub is_contract_call: bool,
49    pub purity: Purity,
50    pub where_clause: Vec<(Ident, Vec<TraitConstraint>)>,
51    pub is_trait_method_dummy: bool,
52    pub is_type_check_finalized: bool,
53    pub kind: TyFunctionDeclKind,
54}
55
56impl TyDeclParsedType for TyFunctionDecl {
57    type ParsedType = FunctionDeclaration;
58}
59
60impl DebugWithEngines for TyFunctionDecl {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
62        write!(
63            f,
64            "{}{:?}{}({}):{}->{}",
65            if self.is_trait_method_dummy {
66                "dummy ".to_string()
67            } else {
68                "".to_string()
69            },
70            self.name,
71            if !self.type_parameters.is_empty() {
72                format!(
73                    "<{}>",
74                    self.type_parameters
75                        .iter()
76                        .map(|p| {
77                            let p = p
78                                .as_type_parameter()
79                                .expect("only works for type parameters");
80                            format!(
81                                "{:?} -> {:?}",
82                                engines.help_out(p.initial_type_id),
83                                engines.help_out(p.type_id)
84                            )
85                        })
86                        .collect::<Vec<_>>()
87                        .join(", ")
88                )
89            } else {
90                "".to_string()
91            },
92            self.parameters
93                .iter()
94                .map(|p| format!(
95                    "{}:{} -> {}",
96                    p.name.as_str(),
97                    engines.help_out(p.type_argument.initial_type_id()),
98                    engines.help_out(p.type_argument.type_id())
99                ))
100                .collect::<Vec<_>>()
101                .join(", "),
102            engines.help_out(self.return_type.initial_type_id()),
103            engines.help_out(self.return_type.type_id()),
104        )
105    }
106}
107
108impl DisplayWithEngines for TyFunctionDecl {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
110        write!(
111            f,
112            "{}{}({}) -> {}",
113            self.name,
114            if !self.type_parameters.is_empty() {
115                format!(
116                    "<{}>",
117                    self.type_parameters
118                        .iter()
119                        .map(|p| {
120                            let p = p
121                                .as_type_parameter()
122                                .expect("only works for type parameters");
123                            format!("{}", engines.help_out(p.initial_type_id))
124                        })
125                        .collect::<Vec<_>>()
126                        .join(", ")
127                )
128            } else {
129                "".to_string()
130            },
131            self.parameters
132                .iter()
133                .map(|p| format!(
134                    "{}: {}",
135                    p.name.as_str(),
136                    engines.help_out(p.type_argument.initial_type_id())
137                ))
138                .collect::<Vec<_>>()
139                .join(", "),
140            engines.help_out(self.return_type.initial_type_id()),
141        )
142    }
143}
144
145impl MaterializeConstGenerics for TyFunctionDecl {
146    fn materialize_const_generics(
147        &mut self,
148        engines: &Engines,
149        handler: &Handler,
150        name: &str,
151        value: &TyExpression,
152    ) -> Result<(), ErrorEmitted> {
153        for param in self.parameters.iter_mut() {
154            param
155                .type_argument
156                .type_id_mut()
157                .materialize_const_generics(engines, handler, name, value)?;
158        }
159        self.return_type
160            .type_id_mut()
161            .materialize_const_generics(engines, handler, name, value)?;
162        self.body
163            .materialize_const_generics(engines, handler, name, value)
164    }
165}
166
167impl DeclRefFunction {
168    /// Makes method with a copy of type_id.
169    /// This avoids altering the type_id already in the type map.
170    /// Without this it is possible to retrieve a method from the type map unify its types and
171    /// the second time it won't be possible to retrieve the same method.
172    pub fn get_method_safe_to_unify(&self, engines: &Engines, type_id: TypeId) -> Self {
173        let decl_engine = engines.de();
174
175        let mut method = (*decl_engine.get_function(self)).clone();
176
177        if let Some(method_implementing_for_typeid) = method.implementing_for_typeid {
178            let mut type_id_type_subst_map = TypeSubstMap::new();
179
180            if let Some(TyDecl::ImplSelfOrTrait(t)) = &method.implementing_type {
181                let impl_self_or_trait = &*engines.de().get(&t.decl_id);
182
183                let mut type_id_type_parameters = vec![];
184                let mut const_generic_parameters = BTreeMap::default();
185                type_id.extract_type_parameters(
186                    engines,
187                    0,
188                    &mut type_id_type_parameters,
189                    &mut const_generic_parameters,
190                    impl_self_or_trait.implementing_for.type_id(),
191                );
192
193                type_id_type_subst_map
194                    .const_generics_materialization
195                    .append(&mut const_generic_parameters);
196
197                for p in impl_self_or_trait
198                    .impl_type_parameters
199                    .iter()
200                    .filter_map(|x| x.as_type_parameter())
201                {
202                    let matches = type_id_type_parameters
203                        .iter()
204                        .filter(|(_, orig_tp)| {
205                            engines.te().get(*orig_tp).eq(
206                                &*engines.te().get(p.type_id),
207                                &PartialEqWithEnginesContext::new(engines),
208                            )
209                        })
210                        .collect::<Vec<_>>();
211
212                    if !matches.is_empty() {
213                        // Adds type substitution for first match only as we can apply only one.
214                        type_id_type_subst_map.insert(p.type_id, matches[0].0);
215                    } else if engines
216                        .te()
217                        .get(impl_self_or_trait.implementing_for.initial_type_id())
218                        .eq(
219                            &*engines.te().get(p.initial_type_id),
220                            &PartialEqWithEnginesContext::new(engines),
221                        )
222                    {
223                        type_id_type_subst_map.insert(p.type_id, type_id);
224                    }
225                }
226            }
227
228            let mut method_type_subst_map = TypeSubstMap::new();
229            method_type_subst_map.extend(&type_id_type_subst_map);
230            method_type_subst_map.insert(method_implementing_for_typeid, type_id);
231
232            method.subst(&SubstTypesContext::new(
233                engines,
234                &method_type_subst_map,
235                true,
236            ));
237
238            return engines
239                .de()
240                .insert(
241                    method.clone(),
242                    engines.de().get_parsed_decl_id(self.id()).as_ref(),
243                )
244                .with_parent(decl_engine, self.id().into());
245        }
246
247        self.clone()
248    }
249}
250
251impl Named for TyFunctionDecl {
252    fn name(&self) -> &Ident {
253        &self.name
254    }
255}
256
257impl IsConcrete for TyFunctionDecl {
258    fn is_concrete(&self, engines: &Engines) -> bool {
259        self.type_parameters
260            .iter()
261            .all(|tp| tp.is_concrete(engines))
262            && self
263                .return_type
264                .type_id()
265                .is_concrete(engines, TreatNumericAs::Concrete)
266            && self.parameters().iter().all(|t| {
267                t.type_argument
268                    .type_id()
269                    .is_concrete(engines, TreatNumericAs::Concrete)
270            })
271    }
272}
273impl declaration::FunctionSignature for TyFunctionDecl {
274    fn parameters(&self) -> &Vec<TyFunctionParameter> {
275        &self.parameters
276    }
277
278    fn return_type(&self) -> &GenericArgument {
279        &self.return_type
280    }
281}
282
283impl EqWithEngines for TyFunctionDecl {}
284impl PartialEqWithEngines for TyFunctionDecl {
285    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
286        self.name == other.name
287            && self.body.eq(&other.body, ctx)
288            && self.parameters.eq(&other.parameters, ctx)
289            && self.return_type.eq(&other.return_type, ctx)
290            && self.type_parameters.eq(&other.type_parameters, ctx)
291            && self.visibility == other.visibility
292            && self.is_contract_call == other.is_contract_call
293            && self.purity == other.purity
294    }
295}
296
297impl HashWithEngines for TyFunctionDecl {
298    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
299        let TyFunctionDecl {
300            name,
301            body,
302            parameters,
303            return_type,
304            type_parameters,
305            visibility,
306            is_contract_call,
307            purity,
308            // these fields are not hashed because they aren't relevant/a
309            // reliable source of obj v. obj distinction
310            call_path: _,
311            span: _,
312            attributes: _,
313            implementing_type: _,
314            implementing_for_typeid: _,
315            where_clause: _,
316            is_trait_method_dummy: _,
317            is_type_check_finalized: _,
318            kind: _,
319        } = self;
320        name.hash(state);
321        body.hash(state, engines);
322        parameters.hash(state, engines);
323        return_type.hash(state, engines);
324        type_parameters.hash(state, engines);
325        visibility.hash(state);
326        is_contract_call.hash(state);
327        purity.hash(state);
328    }
329}
330
331impl SubstTypes for TyFunctionDecl {
332    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
333        let changes = if ctx.subst_function_body {
334            has_changes! {
335                self.type_parameters.subst(ctx);
336                self.parameters.subst(ctx);
337                self.return_type.subst(ctx);
338                self.body.subst(ctx);
339                self.implementing_for_typeid.subst(ctx);
340            }
341        } else {
342            has_changes! {
343                self.type_parameters.subst(ctx);
344                self.parameters.subst(ctx);
345                self.return_type.subst(ctx);
346                self.implementing_for_typeid.subst(ctx);
347            }
348        };
349
350        if let Some(map) = ctx.type_subst_map.as_ref() {
351            let handler = Handler::default();
352            for (name, value) in &map.const_generics_materialization {
353                let _ = self.materialize_const_generics(ctx.engines, &handler, name, value);
354            }
355            HasChanges::Yes
356        } else {
357            changes
358        }
359    }
360}
361
362impl ReplaceDecls for TyFunctionDecl {
363    fn replace_decls_inner(
364        &mut self,
365        decl_mapping: &DeclMapping,
366        handler: &Handler,
367        ctx: &mut TypeCheckContext,
368    ) -> Result<bool, ErrorEmitted> {
369        let mut func_ctx = ctx.by_ref().with_self_type(self.implementing_for_typeid);
370        self.body
371            .replace_decls(decl_mapping, handler, &mut func_ctx)
372    }
373}
374
375impl Spanned for TyFunctionDecl {
376    fn span(&self) -> Span {
377        self.span.clone()
378    }
379}
380
381impl MonomorphizeHelper for TyFunctionDecl {
382    fn type_parameters(&self) -> &[TypeParameter] {
383        &self.type_parameters
384    }
385
386    fn name(&self) -> &Ident {
387        &self.name
388    }
389
390    fn has_self_type_param(&self) -> bool {
391        false
392    }
393}
394
395impl CollectTypesMetadata for TyFunctionDecl {
396    fn collect_types_metadata(
397        &self,
398        handler: &Handler,
399        ctx: &mut CollectTypesMetadataContext,
400    ) -> Result<Vec<TypeMetadata>, ErrorEmitted> {
401        let mut body = vec![];
402        for content in self.body.contents.iter() {
403            body.append(&mut content.collect_types_metadata(handler, ctx)?);
404        }
405        body.append(
406            &mut self
407                .return_type
408                .type_id()
409                .collect_types_metadata(handler, ctx)?,
410        );
411        for p in self.type_parameters.iter() {
412            let p = p
413                .as_type_parameter()
414                .expect("only works for type parameters");
415            body.append(&mut p.type_id.collect_types_metadata(handler, ctx)?);
416        }
417        for param in self.parameters.iter() {
418            body.append(
419                &mut param
420                    .type_argument
421                    .type_id()
422                    .collect_types_metadata(handler, ctx)?,
423            );
424        }
425        Ok(body)
426    }
427}
428
429impl TyFunctionDecl {
430    pub(crate) fn set_implementing_type(&mut self, decl: TyDecl) {
431        self.implementing_type = Some(decl);
432    }
433
434    /// Used to create a stubbed out function when the function fails to
435    /// compile, preventing cascading namespace errors.
436    pub(crate) fn error(decl: &parsed::FunctionDeclaration) -> TyFunctionDecl {
437        let parsed::FunctionDeclaration {
438            name,
439            return_type,
440            span,
441            visibility,
442            purity,
443            where_clause,
444            kind,
445            ..
446        } = decl;
447        TyFunctionDecl {
448            purity: *purity,
449            name: name.clone(),
450            body: <_>::default(),
451            implementing_type: None,
452            implementing_for_typeid: None,
453            span: span.clone(),
454            call_path: CallPath::from(Ident::dummy()),
455            attributes: Default::default(),
456            is_contract_call: false,
457            parameters: Default::default(),
458            visibility: *visibility,
459            return_type: return_type.clone(),
460            type_parameters: Default::default(),
461            where_clause: where_clause.clone(),
462            is_trait_method_dummy: false,
463            is_type_check_finalized: true,
464            kind: match kind {
465                FunctionDeclarationKind::Default => TyFunctionDeclKind::Default,
466                FunctionDeclarationKind::Entry => TyFunctionDeclKind::Entry,
467                FunctionDeclarationKind::Test => TyFunctionDeclKind::Test,
468                FunctionDeclarationKind::Main => TyFunctionDeclKind::Main,
469            },
470        }
471    }
472
473    /// If there are parameters, join their spans. Otherwise, use the fn name span.
474    pub(crate) fn parameters_span(&self) -> Span {
475        if !self.parameters.is_empty() {
476            self.parameters.iter().fold(
477                // TODO: Use Span::join_all().
478                self.parameters[0].name.span(),
479                |acc, TyFunctionParameter { type_argument, .. }| {
480                    Span::join(acc, &type_argument.span())
481                },
482            )
483        } else {
484            self.name.span()
485        }
486    }
487
488    pub fn to_fn_selector_value_untruncated(
489        &self,
490        handler: &Handler,
491        engines: &Engines,
492    ) -> Result<Vec<u8>, ErrorEmitted> {
493        let mut hasher = Sha256::new();
494        let data = self.to_selector_name(handler, engines)?;
495        hasher.update(data);
496        let hash = hasher.finalize();
497        Ok(hash.to_vec())
498    }
499
500    /// Converts a [TyFunctionDecl] into a value that is to be used in contract function
501    /// selectors.
502    /// Hashes the name and parameters using SHA256, and then truncates to four bytes.
503    pub fn to_fn_selector_value(
504        &self,
505        handler: &Handler,
506        engines: &Engines,
507    ) -> Result<[u8; 4], ErrorEmitted> {
508        let hash = self.to_fn_selector_value_untruncated(handler, engines)?;
509        // 4 bytes truncation via copying into a 4 byte buffer
510        let mut buf = [0u8; 4];
511        buf.copy_from_slice(&hash[..4]);
512        Ok(buf)
513    }
514
515    pub fn to_selector_name(
516        &self,
517        handler: &Handler,
518        engines: &Engines,
519    ) -> Result<String, ErrorEmitted> {
520        let named_params = self
521            .parameters
522            .iter()
523            .map(|TyFunctionParameter { type_argument, .. }| {
524                engines
525                    .te()
526                    .to_typeinfo(type_argument.type_id(), &type_argument.span())
527                    .expect("unreachable I think?")
528                    .to_selector_name(handler, engines, &type_argument.span())
529            })
530            .filter_map(|name| name.ok())
531            .collect::<Vec<String>>();
532
533        Ok(format!(
534            "{}({})",
535            self.name.as_str(),
536            named_params.join(","),
537        ))
538    }
539
540    /// Whether or not this function is the default entry point.
541    pub fn is_entry(&self) -> bool {
542        matches!(self.kind, TyFunctionDeclKind::Entry)
543    }
544
545    pub fn is_main(&self) -> bool {
546        matches!(self.kind, TyFunctionDeclKind::Main)
547    }
548
549    /// Whether or not this function is a unit test, i.e. decorated with `#[test]`.
550    pub fn is_test(&self) -> bool {
551        //TODO match kind to Test
552        self.attributes.has_any_of_kind(AttributeKind::Test)
553    }
554
555    pub fn inline(&self) -> Option<Inline> {
556        self.attributes.inline()
557    }
558
559    pub fn is_fallback(&self) -> bool {
560        self.attributes.has_any_of_kind(AttributeKind::Fallback)
561    }
562
563    /// Whether or not this function is a constructor for the type given by `type_id`.
564    ///
565    /// Returns `Some(true)` if the function is surely the constructor and `Some(false)` if
566    /// it is surely not a constructor, and `None` if it cannot decide.
567    pub fn is_constructor(&self, engines: &Engines, type_id: TypeId) -> Option<bool> {
568        if self
569            .parameters
570            .first()
571            .map(|param| param.is_self())
572            .unwrap_or_default()
573        {
574            return Some(false);
575        };
576
577        match &self.implementing_type {
578            Some(TyDecl::ImplSelfOrTrait(t)) => {
579                let unify_check = UnifyCheck::non_dynamic_equality(engines);
580
581                let implementing_for = engines.de().get(&t.decl_id).implementing_for.type_id();
582
583                // TODO: Implement the check in detail for all possible cases (e.g. trait impls for generics etc.)
584                //       and return just the definite `bool` and not `Option<bool>`.
585                //       That would be too much effort at the moment for the immediate practical need of
586                //       error reporting where we suggest obvious most common constructors
587                //       that will be found using this simple check.
588                if unify_check.check(type_id, implementing_for)
589                    && unify_check.check(type_id, self.return_type.type_id())
590                {
591                    Some(true)
592                } else {
593                    None
594                }
595            }
596            _ => Some(false),
597        }
598    }
599
600    pub fn is_from_blanket_impl(&self, engines: &Engines) -> bool {
601        if let Some(TyDecl::ImplSelfOrTrait(existing_impl_trait)) = self.implementing_type.clone() {
602            let existing_trait_decl = engines
603                .de()
604                .get_impl_self_or_trait(&existing_impl_trait.decl_id);
605            if !existing_trait_decl.impl_type_parameters.is_empty()
606                && matches!(
607                    *engines
608                        .te()
609                        .get(existing_trait_decl.implementing_for.type_id()),
610                    TypeInfo::UnknownGeneric { .. }
611                )
612            {
613                return true;
614            }
615        }
616        false
617    }
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct TyFunctionParameter {
622    pub name: Ident,
623    pub is_reference: bool,
624    pub is_mutable: bool,
625    pub mutability_span: Span,
626    pub type_argument: GenericArgument,
627}
628
629impl EqWithEngines for TyFunctionParameter {}
630impl PartialEqWithEngines for TyFunctionParameter {
631    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
632        self.name == other.name
633            && self.type_argument.eq(&other.type_argument, ctx)
634            && self.is_reference == other.is_reference
635            && self.is_mutable == other.is_mutable
636    }
637}
638
639impl HashWithEngines for TyFunctionParameter {
640    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
641        let TyFunctionParameter {
642            name,
643            is_reference,
644            is_mutable,
645            type_argument,
646            // these fields are not hashed because they aren't relevant/a
647            // reliable source of obj v. obj distinction
648            mutability_span: _,
649        } = self;
650        name.hash(state);
651        type_argument.hash(state, engines);
652        is_reference.hash(state);
653        is_mutable.hash(state);
654    }
655}
656
657impl SubstTypes for TyFunctionParameter {
658    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
659        self.type_argument.type_id_mut().subst(ctx)
660    }
661}
662
663impl TyFunctionParameter {
664    pub fn is_self(&self) -> bool {
665        self.name.as_str() == "self"
666    }
667}
668
669#[derive(Clone, Debug, PartialEq, Eq, Hash)]
670pub struct TyFunctionSig {
671    pub return_type: TypeId,
672    pub parameters: Vec<TypeId>,
673    pub type_parameters: Vec<TypeId>,
674}
675
676impl DisplayWithEngines for TyFunctionSig {
677    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
678        write!(f, "{:?}", engines.help_out(self))
679    }
680}
681
682impl DebugWithEngines for TyFunctionSig {
683    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
684        let tp_str = if self.type_parameters.is_empty() {
685            "".to_string()
686        } else {
687            format!(
688                "<{}>",
689                self.type_parameters
690                    .iter()
691                    .map(|p| format!("{}", engines.help_out(p)))
692                    .collect::<Vec<_>>()
693                    .join(", "),
694            )
695        };
696        write!(
697            f,
698            "fn{}({}) -> {}",
699            tp_str,
700            self.parameters
701                .iter()
702                .map(|p| format!("{}", engines.help_out(p)))
703                .collect::<Vec<_>>()
704                .join(", "),
705            engines.help_out(self.return_type),
706        )
707    }
708}
709
710impl TyFunctionSig {
711    pub fn from_fn_decl(fn_decl: &TyFunctionDecl) -> Self {
712        Self {
713            return_type: fn_decl.return_type.type_id(),
714            parameters: fn_decl
715                .parameters
716                .iter()
717                .map(|p| p.type_argument.type_id())
718                .collect::<Vec<_>>(),
719            type_parameters: fn_decl
720                .type_parameters
721                .iter()
722                .filter_map(|x| x.as_type_parameter())
723                .map(|p| p.type_id)
724                .collect::<Vec<_>>(),
725        }
726    }
727
728    pub fn is_concrete(&self, engines: &Engines) -> bool {
729        self.return_type
730            .is_concrete(engines, TreatNumericAs::Concrete)
731            && self
732                .parameters
733                .iter()
734                .all(|p| p.is_concrete(engines, TreatNumericAs::Concrete))
735            && self
736                .type_parameters
737                .iter()
738                .all(|p| p.is_concrete(engines, TreatNumericAs::Concrete))
739    }
740
741    /// Returns a String representing the function.
742    /// When the function is monomorphized the returned String is unique.
743    /// Two monomorphized functions that generate the same String can be assumed to be the same.
744    pub fn get_type_str(&self, engines: &Engines) -> String {
745        let tp_str = if self.type_parameters.is_empty() {
746            "".to_string()
747        } else {
748            format!(
749                "<{}>",
750                self.type_parameters
751                    .iter()
752                    .map(|p| p.get_type_str(engines))
753                    .collect::<Vec<_>>()
754                    .join(", "),
755            )
756        };
757        format!(
758            "fn{}({}) -> {}",
759            tp_str,
760            self.parameters
761                .iter()
762                .map(|p| p.get_type_str(engines))
763                .collect::<Vec<_>>()
764                .join(", "),
765            self.return_type.get_type_str(engines),
766        )
767    }
768}