sway_core/language/ty/
program.rs

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