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