Skip to main content

sway_core/language/ty/
program.rs

1use crate::{
2    decl_engine::*,
3    fuel_prelude::fuel_tx::StorageSlot,
4    language::{parsed, ty::*, Purity, Visibility},
5    namespace::{check_impls_for_overlap, check_orphan_rules_for_impls, TraitMap},
6    semantic_analysis::namespace,
7    transform::AllowDeprecatedState,
8    type_system::*,
9    types::*,
10    Engines,
11};
12use std::sync::Arc;
13use sway_error::{
14    error::{CompileError, TypeNotAllowedReason},
15    handler::{ErrorEmitted, Handler},
16};
17use sway_features::ExperimentalFeatures;
18use sway_types::*;
19
20#[derive(Debug, Clone)]
21pub struct TyProgram {
22    pub kind: TyProgramKind,
23    pub root_module: TyModule,
24    pub namespace: namespace::Namespace,
25    pub declarations: Vec<TyDecl>,
26    pub configurables: Vec<TyConfigurableDecl>,
27    pub storage_slots: Vec<StorageSlot>,
28    pub logged_types: Vec<(LogId, TypeId)>,
29    pub messages_types: Vec<(MessageId, TypeId)>,
30}
31
32fn get_type_not_allowed_error(
33    engines: &Engines,
34    type_id: TypeId,
35    spanned: &impl Spanned,
36    f: impl Fn(&TypeInfo) -> Option<TypeNotAllowedReason>,
37) -> Option<CompileError> {
38    let types = type_id.extract_any_including_self(engines, &|t| f(t).is_some(), vec![], 0);
39
40    let (id, _) = types.into_iter().next()?;
41    let t = engines.te().get(id);
42
43    Some(CompileError::TypeNotAllowed {
44        reason: f(&t)?,
45        span: spanned.span(),
46    })
47}
48
49fn check_no_ref_main(engines: &Engines, handler: &Handler, main_function: &DeclId<TyFunctionDecl>) {
50    let main_function = engines.de().get_function(main_function);
51    for param in main_function.parameters.iter() {
52        if param.is_reference && param.is_mutable {
53            handler.emit_err(CompileError::RefMutableNotAllowedInMain {
54                param_name: param.name.clone(),
55                span: param.name.span(),
56            });
57        }
58    }
59}
60
61impl TyProgram {
62    pub fn validate_coherence(
63        handler: &Handler,
64        engines: &Engines,
65        root: &TyModule,
66        root_namespace: &mut namespace::Namespace,
67    ) -> Result<(), ErrorEmitted> {
68        // check orphan rules for all traits
69        check_orphan_rules_for_impls(handler, engines, root_namespace.current_package_ref())?;
70
71        // check trait overlap
72        let mut unified_trait_map = root_namespace
73            .current_package_ref()
74            .root_module()
75            .root_lexical_scope()
76            .items
77            .implemented_traits
78            .clone();
79
80        Self::validate_coherence_overlap(
81            handler,
82            engines,
83            root,
84            root_namespace,
85            &mut unified_trait_map,
86        )?;
87
88        Ok(())
89    }
90
91    pub fn validate_coherence_overlap(
92        handler: &Handler,
93        engines: &Engines,
94        module: &TyModule,
95        root_namespace: &mut namespace::Namespace,
96        unified_trait_map: &mut TraitMap,
97    ) -> Result<(), ErrorEmitted> {
98        let other_trait_map = unified_trait_map.clone();
99        check_impls_for_overlap(unified_trait_map, handler, other_trait_map, engines)?;
100
101        for (submod_name, submodule) in module.submodules.iter() {
102            root_namespace.push_submodule(
103                handler,
104                engines,
105                submod_name.clone(),
106                Visibility::Public,
107                submodule.mod_name_span.clone(),
108                false,
109            )?;
110
111            Self::validate_coherence_overlap(
112                handler,
113                engines,
114                &submodule.module,
115                root_namespace,
116                unified_trait_map,
117            )?;
118
119            root_namespace.pop_submodule();
120        }
121
122        Ok(())
123    }
124
125    /// Validate the root module given the expected program kind.
126    pub fn validate_root(
127        handler: &Handler,
128        engines: &Engines,
129        root: &TyModule,
130        kind: parsed::TreeType,
131        package_name: &str,
132        experimental: ExperimentalFeatures,
133    ) -> Result<(TyProgramKind, Vec<TyDecl>, Vec<TyConfigurableDecl>), ErrorEmitted> {
134        // Extract program-kind-specific properties from the root nodes.
135
136        let ty_engine = engines.te();
137        let decl_engine = engines.de();
138
139        // Validate all submodules
140        let mut configurables = vec![];
141        for (_, submodule) in &root.submodules {
142            let _ = Self::validate_root(
143                handler,
144                engines,
145                &submodule.module,
146                parsed::TreeType::Library,
147                package_name,
148                experimental,
149            );
150        }
151
152        let mut entries = Vec::new();
153        let mut mains = Vec::new();
154        let mut declarations = Vec::<TyDecl>::new();
155        let mut abi_entries = Vec::new();
156        let mut fn_declarations = std::collections::HashSet::new();
157
158        for node in &root.all_nodes {
159            match &node.content {
160                TyAstNodeContent::Declaration(TyDecl::FunctionDecl(FunctionDecl { decl_id })) => {
161                    let func = decl_engine.get_function(decl_id);
162
163                    match func.kind {
164                        TyFunctionDeclKind::Main => mains.push(*decl_id),
165                        TyFunctionDeclKind::Entry => entries.push(*decl_id),
166                        _ => {}
167                    }
168
169                    if !fn_declarations.insert(func.name.clone()) {
170                        handler.emit_err(CompileError::MultipleDefinitionsOfFunction {
171                            name: func.name.clone(),
172                            span: func.name.span(),
173                        });
174                    }
175
176                    declarations.push(TyDecl::FunctionDecl(FunctionDecl { decl_id: *decl_id }));
177                }
178                TyAstNodeContent::Declaration(TyDecl::ConfigurableDecl(ConfigurableDecl {
179                    decl_id,
180                    ..
181                })) => {
182                    let decl = (*decl_engine.get_configurable(decl_id)).clone();
183                    configurables.push(decl);
184                }
185                // ABI entries are all functions declared in impl_traits on the contract type
186                // itself, except for ABI supertraits, which do not expose their methods to
187                // the user
188                TyAstNodeContent::Declaration(TyDecl::ImplSelfOrTrait(ImplSelfOrTrait {
189                    decl_id,
190                    ..
191                })) => {
192                    let impl_trait_decl = decl_engine.get_impl_self_or_trait(decl_id);
193                    let TyImplSelfOrTrait {
194                        items,
195                        implementing_for,
196                        trait_decl_ref,
197                        ..
198                    } = &*impl_trait_decl;
199                    if matches!(
200                        &*ty_engine.get(implementing_for.type_id),
201                        TypeInfo::Contract
202                    ) {
203                        // add methods to the ABI only if they come from an ABI implementation
204                        // and not a (super)trait implementation for Contract
205                        if let Some(trait_decl_ref) = trait_decl_ref {
206                            if matches!(*trait_decl_ref.id(), InterfaceDeclId::Abi(_)) {
207                                for item in items {
208                                    match item {
209                                        TyImplItem::Fn(method_ref) => {
210                                            abi_entries.push(*method_ref.id());
211                                        }
212                                        TyImplItem::Constant(const_ref) => {
213                                            declarations.push(TyDecl::ConstantDecl(ConstantDecl {
214                                                decl_id: *const_ref.id(),
215                                            }));
216                                        }
217                                        TyImplItem::Type(type_ref) => {
218                                            declarations.push(TyDecl::TraitTypeDecl(
219                                                TraitTypeDecl {
220                                                    decl_id: *type_ref.id(),
221                                                },
222                                            ));
223                                        }
224                                    }
225                                }
226                            }
227                        }
228                    }
229                }
230                // XXX we're excluding the above ABI methods, is that OK?
231                TyAstNodeContent::Declaration(decl) => {
232                    declarations.push(decl.clone());
233                }
234                _ => {}
235            };
236        }
237
238        // Some checks that are specific to non-contracts
239        if kind != parsed::TreeType::Contract {
240            // impure functions are disallowed in non-contracts
241            if !matches!(kind, parsed::TreeType::Library) {
242                for err in disallow_impure_functions(decl_engine, &declarations, &entries) {
243                    handler.emit_err(err);
244                }
245            }
246
247            // `storage` declarations are not allowed in non-contracts
248            let storage_decl = declarations
249                .iter()
250                .find(|decl| matches!(decl, TyDecl::StorageDecl { .. }));
251
252            if let Some(TyDecl::StorageDecl(StorageDecl { decl_id })) = storage_decl {
253                handler.emit_err(CompileError::StorageDeclarationInNonContract {
254                    program_kind: format!("{kind}"),
255                    span: engines.de().get(decl_id).span.clone(),
256                });
257            }
258        }
259
260        // Perform other validation based on the tree type.
261        let typed_program_kind = match kind {
262            parsed::TreeType::Contract => {
263                // Types containing raw_ptr are not allowed in storage (e.g Vec)
264                for decl in declarations.iter() {
265                    if let TyDecl::StorageDecl(StorageDecl { decl_id }) = decl {
266                        let storage_decl = decl_engine.get_storage(decl_id);
267                        for field in storage_decl.fields.iter() {
268                            if let Some(error) = get_type_not_allowed_error(
269                                engines,
270                                field.type_argument.type_id,
271                                &field.type_argument,
272                                |t| match t {
273                                    TypeInfo::StringSlice => {
274                                        Some(TypeNotAllowedReason::StringSliceInConfigurables)
275                                    }
276                                    TypeInfo::RawUntypedPtr => Some(
277                                        TypeNotAllowedReason::TypeNotAllowedInContractStorage {
278                                            ty: engines.help_out(t).to_string(),
279                                        },
280                                    ),
281                                    _ => None,
282                                },
283                            ) {
284                                handler.emit_err(error);
285                            }
286                        }
287                    }
288                }
289
290                TyProgramKind::Contract {
291                    entry_function: if experimental.new_encoding {
292                        if entries.len() != 1 {
293                            return Err(handler.emit_err(CompileError::CouldNotGenerateEntry {
294                                span: Span::dummy(),
295                            }));
296                        }
297                        Some(entries[0])
298                    } else {
299                        None
300                    },
301                    abi_entries,
302                }
303            }
304            parsed::TreeType::Library => {
305                if !configurables.is_empty() {
306                    handler.emit_err(CompileError::ConfigurableInLibrary {
307                        span: configurables[0].call_path.suffix.span(),
308                    });
309                }
310                TyProgramKind::Library {
311                    name: package_name.to_string(),
312                }
313            }
314            parsed::TreeType::Predicate => {
315                if mains.is_empty() {
316                    return Err(
317                        handler.emit_err(CompileError::NoPredicateMainFunction(root.span.clone()))
318                    );
319                }
320
321                if mains.len() > 1 {
322                    let mut last_error = None;
323                    for m in mains.iter().skip(1) {
324                        let mains_last = decl_engine.get_function(m);
325                        last_error = Some(handler.emit_err(
326                            CompileError::MultipleDefinitionsOfFunction {
327                                name: mains_last.name.clone(),
328                                span: mains_last.name.span(),
329                            },
330                        ));
331                    }
332                    return Err(last_error.unwrap());
333                }
334
335                // check if no ref mut arguments passed to a `main()` in a `script` or `predicate`.
336                check_no_ref_main(engines, handler, &mains[0]);
337
338                let (entry_fn_id, main_fn_id) = if experimental.new_encoding {
339                    if entries.len() != 1 {
340                        return Err(handler.emit_err(CompileError::CouldNotGenerateEntry {
341                            span: Span::dummy(),
342                        }));
343                    }
344                    (entries[0], mains[0])
345                } else {
346                    assert!(entries.is_empty());
347                    (mains[0], mains[0])
348                };
349
350                let main_fn = decl_engine.get(&main_fn_id);
351                if !ty_engine.get(main_fn.return_type.type_id).is_bool() {
352                    handler.emit_err(CompileError::PredicateMainDoesNotReturnBool(
353                        main_fn.span.clone(),
354                    ));
355                }
356
357                TyProgramKind::Predicate {
358                    entry_function: entry_fn_id,
359                    main_function: main_fn_id,
360                }
361            }
362            parsed::TreeType::Script => {
363                // A script must have exactly one main function
364                if mains.is_empty() {
365                    return Err(
366                        handler.emit_err(CompileError::NoScriptMainFunction(root.span.clone()))
367                    );
368                }
369
370                if mains.len() > 1 {
371                    let mut last_error = None;
372                    for m in mains.iter().skip(1) {
373                        let mains_last = decl_engine.get_function(m);
374                        last_error = Some(handler.emit_err(
375                            CompileError::MultipleDefinitionsOfFunction {
376                                name: mains_last.name.clone(),
377                                span: mains_last.name.span(),
378                            },
379                        ));
380                    }
381                    return Err(last_error.unwrap());
382                }
383
384                // check if no ref mut arguments passed to a `main()` in a `script` or `predicate`.
385                check_no_ref_main(engines, handler, &mains[0]);
386
387                let (entry_fn_id, main_fn_id) = if experimental.new_encoding {
388                    if entries.len() != 1 {
389                        return Err(handler.emit_err(CompileError::CouldNotGenerateEntry {
390                            span: Span::dummy(),
391                        }));
392                    }
393                    (entries[0], mains[0])
394                } else {
395                    assert!(entries.is_empty());
396                    (mains[0], mains[0])
397                };
398
399                // On encoding v0, we cannot accept/return ptrs, slices etc...
400                if !experimental.new_encoding {
401                    let main_fn = decl_engine.get(&main_fn_id);
402                    for p in main_fn.parameters() {
403                        if let Some(error) = get_type_not_allowed_error(
404                            engines,
405                            p.type_argument.type_id,
406                            &p.type_argument,
407                            |t| match t {
408                                TypeInfo::StringSlice => {
409                                    Some(TypeNotAllowedReason::StringSliceInMainParameters)
410                                }
411                                TypeInfo::RawUntypedSlice => {
412                                    Some(TypeNotAllowedReason::NestedSliceReturnNotAllowedInMain)
413                                }
414                                _ => None,
415                            },
416                        ) {
417                            handler.emit_err(error);
418                        }
419                    }
420
421                    // Check main return type is valid
422                    if let Some(error) = get_type_not_allowed_error(
423                        engines,
424                        main_fn.return_type.type_id,
425                        &main_fn.return_type,
426                        |t| match t {
427                            TypeInfo::StringSlice => {
428                                Some(TypeNotAllowedReason::StringSliceInMainReturn)
429                            }
430                            TypeInfo::RawUntypedSlice => {
431                                Some(TypeNotAllowedReason::NestedSliceReturnNotAllowedInMain)
432                            }
433                            _ => None,
434                        },
435                    ) {
436                        // Let main return `raw_slice` directly
437                        if !matches!(
438                            &*engines.te().get(main_fn.return_type.type_id),
439                            TypeInfo::RawUntypedSlice
440                        ) {
441                            handler.emit_err(error);
442                        }
443                    }
444                }
445
446                TyProgramKind::Script {
447                    entry_function: entry_fn_id,
448                    main_function: main_fn_id,
449                }
450            }
451        };
452
453        //configurables and constant cannot be str slice
454        for c in configurables.iter() {
455            if let Some(error) = get_type_not_allowed_error(
456                engines,
457                c.return_type,
458                &c.type_ascription,
459                |t| match t {
460                    TypeInfo::StringSlice => Some(TypeNotAllowedReason::StringSliceInConfigurables),
461                    TypeInfo::Slice(_) => Some(TypeNotAllowedReason::SliceInConst),
462                    _ => None,
463                },
464            ) {
465                handler.emit_err(error);
466            }
467        }
468
469        // verify all constants
470        for decl in root.iter_constants(decl_engine).iter() {
471            let decl = decl_engine.get_constant(&decl.decl_id);
472            let e =
473                get_type_not_allowed_error(engines, decl.return_type, &decl.type_ascription, |t| {
474                    match t {
475                        TypeInfo::StringSlice => Some(TypeNotAllowedReason::StringSliceInConst),
476                        TypeInfo::Slice(_) => Some(TypeNotAllowedReason::SliceInConst),
477                        _ => None,
478                    }
479                });
480            if let Some(error) = e {
481                handler.emit_err(error);
482            }
483        }
484
485        Ok((typed_program_kind, declarations, configurables))
486    }
487
488    /// All test function declarations within the program.
489    pub fn test_fns<'a: 'b, 'b>(
490        &'b self,
491        decl_engine: &'a DeclEngine,
492    ) -> impl 'b + Iterator<Item = (Arc<TyFunctionDecl>, DeclRefFunction)> {
493        self.root_module.test_fns_recursive(decl_engine)
494    }
495
496    pub fn check_deprecated(&self, engines: &Engines, handler: &Handler) {
497        let mut allow_deprecated = AllowDeprecatedState::default();
498        self.root_module
499            .check_deprecated(engines, handler, &mut allow_deprecated);
500    }
501
502    pub fn check_recursive(
503        &self,
504        engines: &Engines,
505        handler: &Handler,
506    ) -> Result<(), ErrorEmitted> {
507        self.root_module.check_recursive(engines, handler)
508    }
509}
510
511impl CollectTypesMetadata for TyProgram {
512    /// Collect various type information such as unresolved types and types of logged data
513    fn collect_types_metadata(
514        &self,
515        handler: &Handler,
516        ctx: &mut CollectTypesMetadataContext,
517    ) -> Result<Vec<TypeMetadata>, ErrorEmitted> {
518        let decl_engine = ctx.engines.de();
519        let mut metadata = vec![];
520
521        // First, look into all entry points that are not unit tests.
522        match &self.kind {
523            // For scripts and predicates, collect metadata for all the types starting with
524            // `main()` as the only entry point
525            TyProgramKind::Script {
526                entry_function: main_function,
527                ..
528            }
529            | TyProgramKind::Predicate {
530                entry_function: main_function,
531                ..
532            } => {
533                let main_function = decl_engine.get_function(main_function);
534                metadata.append(&mut main_function.collect_types_metadata(handler, ctx)?);
535            }
536            // For contracts, collect metadata for all the types starting with each ABI method as
537            // an entry point.
538            TyProgramKind::Contract {
539                abi_entries,
540                entry_function: main_function,
541            } => {
542                if let Some(main_function) = main_function {
543                    let entry = decl_engine.get_function(main_function);
544                    metadata.append(&mut entry.collect_types_metadata(handler, ctx)?);
545                }
546
547                for entry in abi_entries.iter() {
548                    let entry = decl_engine.get_function(entry);
549                    metadata.append(&mut entry.collect_types_metadata(handler, ctx)?);
550                }
551            }
552            // For libraries, collect metadata for all the types starting with each `pub` node as
553            // an entry point. Also dig into all the submodules of a library because nodes in those
554            // submodules can also be entry points.
555            TyProgramKind::Library { .. } => {
556                for module in std::iter::once(&self.root_module).chain(
557                    self.root_module
558                        .submodules_recursive()
559                        .map(|(_, submod)| &*submod.module),
560                ) {
561                    for node in module.all_nodes.iter() {
562                        let is_generic_function = node.is_generic_function(decl_engine);
563                        if node.is_public(decl_engine) {
564                            let node_metadata = node.collect_types_metadata(handler, ctx)?;
565                            metadata.append(
566                                &mut node_metadata
567                                    .iter()
568                                    .filter(|m| {
569                                        // Generic functions are allowed to have unresolved types
570                                        // so filter those
571                                        !(is_generic_function
572                                            && matches!(m, TypeMetadata::UnresolvedType(..)))
573                                    })
574                                    .cloned()
575                                    .collect::<Vec<TypeMetadata>>(),
576                            );
577                        }
578                    }
579                }
580            }
581        }
582
583        // Now consider unit tests: all unit test are considered entry points regardless of the
584        // program type
585        for module in std::iter::once(&self.root_module).chain(
586            self.root_module
587                .submodules_recursive()
588                .map(|(_, submod)| &*submod.module),
589        ) {
590            for node in module.all_nodes.iter() {
591                if node.is_test_function(decl_engine) {
592                    metadata.append(&mut node.collect_types_metadata(handler, ctx)?);
593                }
594            }
595        }
596
597        Ok(metadata)
598    }
599}
600
601#[derive(Clone, Debug)]
602pub enum TyProgramKind {
603    Contract {
604        entry_function: Option<DeclId<TyFunctionDecl>>,
605        abi_entries: Vec<DeclId<TyFunctionDecl>>,
606    },
607    Library {
608        name: String,
609    },
610    Predicate {
611        entry_function: DeclId<TyFunctionDecl>,
612        main_function: DeclId<TyFunctionDecl>,
613    },
614    Script {
615        entry_function: DeclId<TyFunctionDecl>,
616        main_function: DeclId<TyFunctionDecl>,
617    },
618}
619
620impl TyProgramKind {
621    /// The parse tree type associated with this program kind.
622    pub fn tree_type(&self) -> parsed::TreeType {
623        match self {
624            TyProgramKind::Contract { .. } => parsed::TreeType::Contract,
625            TyProgramKind::Library { .. } => parsed::TreeType::Library,
626            TyProgramKind::Predicate { .. } => parsed::TreeType::Predicate,
627            TyProgramKind::Script { .. } => parsed::TreeType::Script,
628        }
629    }
630    /// Used for project titles in `forc doc`.
631    pub fn as_title_str(&self) -> &str {
632        match self {
633            TyProgramKind::Contract { .. } => "Contract",
634            TyProgramKind::Library { .. } => "Library",
635            TyProgramKind::Predicate { .. } => "Predicate",
636            TyProgramKind::Script { .. } => "Script",
637        }
638    }
639}
640
641fn disallow_impure_functions(
642    decl_engine: &DeclEngine,
643    declarations: &[TyDecl],
644    mains: &[DeclId<TyFunctionDecl>],
645) -> Vec<CompileError> {
646    let mut errs: Vec<CompileError> = vec![];
647    let fn_decls = declarations
648        .iter()
649        .filter_map(|decl| match decl {
650            TyDecl::FunctionDecl(FunctionDecl { decl_id, .. }) => Some(*decl_id),
651            _ => None,
652        })
653        .chain(mains.to_owned());
654    let mut err_purity = fn_decls
655        .filter_map(|decl_id| {
656            let fn_decl = decl_engine.get_function(&decl_id);
657            let TyFunctionDecl { purity, name, .. } = &*fn_decl;
658            if *purity != Purity::Pure {
659                Some(CompileError::ImpureInNonContract { span: name.span() })
660            } else {
661                None
662            }
663        })
664        .collect::<Vec<_>>();
665    errs.append(&mut err_purity);
666    errs
667}