diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea17447cf90..237a7a07e2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -475,6 +475,10 @@ jobs: run: cargo run --locked --release -p forc -- build --experimental storage_domains --release --locked --path ./test/src/sdk-harness - name: Cargo Test sway-lib-std - Experimental Feature 'storage_domains' run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture + - name: Build All Tests - Experimental Feature 'error_type' + run: cargo run --locked --release -p forc -- build --experimental error_type --release --locked --path ./test/src/sdk-harness + - name: Cargo Test sway-lib-std - Experimental Feature 'error_type' + run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture forc-run-benchmarks: runs-on: buildjet-4vcpu-ubuntu-2204 @@ -541,14 +545,20 @@ jobs: run: forc build --path sway-lib-core && forc test --path sway-lib-core - name: Run Core Unit Tests - Experimental feature 'storage_domains' run: forc build --experimental storage_domains --path sway-lib-core && forc test --experimental storage_domains --path sway-lib-core + - name: Run Core Unit Tests - Experimental feature 'error_type' + run: forc build --experimental error_type --path sway-lib-core && forc test --experimental error_type --path sway-lib-core - name: Run Std Unit Tests run: forc build --path sway-lib-std && forc test --path sway-lib-std - name: Run Std Unit Tests - Experimental feature 'storage_domains' run: forc build --experimental storage_domains --path sway-lib-std && forc test --experimental storage_domains --path sway-lib-std + - name: Run Std Unit Tests - Experimental feature 'error_type' + run: forc build --experimental error_type --path sway-lib-std && forc test --experimental error_type --path sway-lib-std - name: Run In Language Unit Tests run: forc build --path test/src/in_language_tests && forc test --path test/src/in_language_tests - name: Run In Language Unit Tests - Experimental feature 'storage_domains' run: forc build --experimental storage_domains --path test/src/in_language_tests && forc test --experimental storage_domains --path test/src/in_language_tests + - name: Run In Language Unit Tests - Experimental feature 'error_type' + run: forc build --experimental error_type --path test/src/in_language_tests && forc test --experimental error_type --path test/src/in_language_tests forc-pkg-fuels-deps-check: runs-on: buildjet-4vcpu-ubuntu-2204 diff --git a/docs/book/src/advanced/traits.md b/docs/book/src/advanced/traits.md index ed87d3b3732..2356716588e 100644 --- a/docs/book/src/advanced/traits.md +++ b/docs/book/src/advanced/traits.md @@ -134,6 +134,43 @@ trait MyTrait { Check the `associated types` section on [associated types](./associated_types.md) page. +## Trait Constraints + +When writing generic code, you can constraint the choice of types for a generic argument by using the `where` keyword. The `where` keyword specifies which traits the concrete generic parameter must implement. In the below example, the function `expects_some_trait` can be called only if the parameter `t` is of a type that has `SomeTrait` implemented. To call the `expects_both_traits`, parameter `t` must be of a type that implements _both_ `SomeTrait` and `SomeOtherTrait`. + +```sway +trait SomeTrait { } +trait SomeOtherTrait { } + +fn expects_some_trait(t: T) where T: SomeTrait { + // ... +} + +fn expects_some_other_trait(t: T) where T: SomeOtherTrait { + // ... +} + +fn expects_both_traits(t: T) where T: SomeTrait + SomeOtherTrait { + // ... +} +``` + +## Marker Traits + +Sway types can be classified in various ways according to their intrinsic properties. These classifications are represented as marker traits. Marker traits are implemented by the compiler and cannot be explicitly implemented in code. + +E.g., all types whose instances can be used in the `panic` expression automatically implement the `Error` marker trait. We can use that trait, e.g., to specify that a generic argument must be compatible with the `panic` expression: + +```sway +fn panic_with_error(err: E) where E: Error { + panic err; +} +``` + +> **Note** `panic` expression and error types [have not yet been implemented](https://github.com/FuelLabs/sway/issues/6765) + +All marker traits are defined in the `core::marker` module. + ## Use Cases ### Custom Types (structs, enums) @@ -160,8 +197,6 @@ fn play_game_with_deck(a: Vec) where T: Card { } ``` -> **Note** Trait constraints (i.e. using the `where` keyword) [have not yet been implemented](https://github.com/FuelLabs/sway/issues/970) - Now, if you want to use the function `play_game_with_deck` with your struct, you must implement `Card` for your struct. Note that the following code example assumes a dependency _games_ has been included in the `Forc.toml` file. ```sway diff --git a/sway-core/src/language/call_path.rs b/sway-core/src/language/call_path.rs index c8abab9c896..9ddbc5a3fd9 100644 --- a/sway-core/src/language/call_path.rs +++ b/sway-core/src/language/call_path.rs @@ -4,7 +4,7 @@ use crate::{ OrdWithEnginesContext, PartialEqWithEngines, PartialEqWithEnginesContext, }, parsed::QualifiedPathType, - Engines, Ident, Namespace, + Engines, Ident, Namespace, TypeArgument, }; use serde::{Deserialize, Serialize}; use std::{ @@ -284,20 +284,26 @@ impl Spanned for CallPath { if self.prefixes.is_empty() { self.suffix.span() } else { + let suffix_span = self.suffix.span(); let mut prefixes_spans = self .prefixes .iter() .map(|x| x.span()) - //LOC below should be removed when #21 goes in + // Depending on how the call path is constructed, we + // might have a situation that the parts do not belong + // to the same source and do not have the same source id. + // In that case, we will take only the suffix' span, as + // the span for the whole call path. Otherwise, we join + // the spans of all the parts. .filter(|x| { - Arc::ptr_eq(x.src(), self.suffix.span().src()) - && x.source_id() == self.suffix.span().source_id() + Arc::ptr_eq(x.src(), suffix_span.src()) + && x.source_id() == suffix_span.source_id() }) .peekable(); if prefixes_spans.peek().is_some() { - Span::join(Span::join_all(prefixes_spans), &self.suffix.span()) + Span::join(Span::join_all(prefixes_spans), &suffix_span) } else { - self.suffix.span() + suffix_span } } } @@ -391,6 +397,33 @@ impl CallPath { } converted } + + /// Create a string form of the given [CallPath] and zero or more [TypeArgument]s. + /// The returned string is convenient for displaying full names, including generic arguments, in help messages. + /// E.g.: + /// - `some::module::SomeType` + /// - `some::module::SomeGenericType` + /// + /// Note that the trailing arguments are never separated by `::` from the suffix. + pub(crate) fn to_string_with_args(&self, engines: &Engines, args: &[TypeArgument]) -> String { + let args = args + .iter() + .map(|type_arg| engines.help_out(type_arg).to_string()) + .collect::>() + .join(", "); + + format!( + "{}{}", + // TODO: Replace with a context aware string representation of the path + // once https://github.com/FuelLabs/sway/issues/6873 is fixed. + &self, + if args.is_empty() { + String::new() + } else { + format!("<{args}>") + } + ) + } } impl CallPath { diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 322e1ae6660..ff2e4a0b886 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -17,6 +17,7 @@ mod debug_generation; pub mod decl_engine; pub mod ir_generation; pub mod language; +pub mod marker_traits; mod metadata; pub mod query_engine; pub mod semantic_analysis; diff --git a/sway-core/src/marker_traits.rs b/sway-core/src/marker_traits.rs new file mode 100644 index 00000000000..b04feaeafde --- /dev/null +++ b/sway-core/src/marker_traits.rs @@ -0,0 +1,35 @@ +use sway_types::{Ident, SourceEngine}; + +use crate::{ + language::{parsed::ImplSelfOrTrait, ty::TyTraitDecl, CallPathType}, + namespace::Module, +}; + +impl TyTraitDecl { + pub(crate) fn is_marker_trait(&self) -> bool { + assert!( + matches!(self.call_path.callpath_type, CallPathType::Full), + "call paths of trait declarations must always be full paths" + ); + + is_core_marker_module_path(&self.call_path.prefixes) + } +} + +impl Module { + pub(crate) fn is_core_marker_module(&self) -> bool { + is_core_marker_module_path(self.mod_path()) + } +} + +impl ImplSelfOrTrait { + pub(crate) fn is_autogenerated(&self, source_engine: &SourceEngine) -> bool { + source_engine + .is_span_in_autogenerated(&self.block_span) + .unwrap_or(false) + } +} + +fn is_core_marker_module_path(path: &[Ident]) -> bool { + path.len() == 2 && path[0].as_str() == "core" && path[1].as_str() == "marker" +} diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs similarity index 72% rename from sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs rename to sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs index 735484b8479..7fed9b3407d 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs @@ -1,117 +1,30 @@ use crate::{ asm_generation::fuel::compiler_constants::MISMATCHED_SELECTOR_REVERT_CODE, decl_engine::{DeclEngineGet, DeclId}, - engine_threading::SpannedWithEngines, language::{ - parsed::{self, AstNodeContent, Declaration, FunctionDeclarationKind}, + parsed::FunctionDeclarationKind, ty::{self, TyAstNode, TyDecl, TyEnumDecl, TyFunctionDecl, TyStructDecl}, Purity, }, - semantic_analysis::TypeCheckContext, - Engines, TypeArgument, TypeInfo, TypeParameter, + Engines, TypeInfo, TypeParameter, }; use std::collections::BTreeMap; use sway_error::{ error::CompileError, handler::{ErrorEmitted, Handler}, }; -use sway_parse::Parse; use sway_types::{BaseIdent, Named, ProgramId, Span, Spanned}; -/// Contains all information needed to implement AbiEncode -pub struct EncodingAutoImplContext<'a, 'b> -where - 'a: 'b, -{ - ctx: &'b mut TypeCheckContext<'a>, -} +#[derive(Default)] +pub struct AbiEncodingAutoImplInfo {} -impl<'a, 'b> EncodingAutoImplContext<'a, 'b> +pub type AbiEncodingAutoImplContext<'a, 'b> = + super::AutoImplContext<'a, 'b, AbiEncodingAutoImplInfo>; + +impl<'a, 'b> AbiEncodingAutoImplContext<'a, 'b> where 'a: 'b, { - pub fn new(ctx: &'b mut TypeCheckContext<'a>) -> Option { - Some(Self { ctx }) - } - - fn parse(engines: &Engines, program_id: Option, input: &str) -> T - where - T: Parse, - { - // Uncomment this to see what is being generated - // println!("{}", input); - - let handler = <_>::default(); - let source_id = - program_id.map(|program_id| engines.se().get_autogenerated_source_id(program_id)); - - let ts = sway_parse::lex( - &handler, - &std::sync::Arc::from(input), - 0, - input.len(), - source_id, - ) - .unwrap(); - let mut p = sway_parse::Parser::new(&handler, &ts); - p.check_double_underscore = false; - - let r = p.parse(); - - assert!(!handler.has_errors(), "{:?}", handler); - assert!(!handler.has_warnings(), "{:?}", handler); - - assert!(!p.has_errors()); - assert!(!p.has_warnings()); - - r.unwrap() - } - - fn generate_type_parameters_declaration_code( - &self, - type_parameters: &[TypeParameter], - ) -> String { - if type_parameters.is_empty() { - String::new() - } else { - format!( - "<{}>", - itertools::intersperse(type_parameters.iter().map(|x| { x.name.as_str() }), ", ") - .collect::() - ) - } - } - - fn generate_type_parameters_constraints_code( - &self, - type_parameters: &[TypeParameter], - extra_constraint: &str, - ) -> String { - let mut code = String::new(); - - for t in type_parameters.iter() { - code.push_str(&format!( - "{}: {},\n", - t.name.as_str(), - itertools::intersperse( - [extra_constraint].into_iter().chain( - t.trait_constraints - .iter() - .map(|x| x.trait_name.suffix.as_str()) - ), - " + " - ) - .collect::() - )); - } - - if !code.is_empty() { - code = format!(" where {code}\n"); - } - - code - } - fn generate_abi_encode_code( &self, name: &BaseIdent, @@ -276,164 +189,6 @@ where format!("let buffer = match self {{ {arms} }};") } - pub fn parse_fn_to_ty_ast_node( - &mut self, - engines: &Engines, - program_id: Option, - kind: FunctionDeclarationKind, - code: &str, - ) -> Result { - let mut ctx = crate::transform::to_parsed_lang::Context::new( - crate::BuildTarget::Fuel, - self.ctx.experimental, - ); - - let handler = Handler::default(); - - let item = Self::parse(engines, program_id, code); - let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes( - &mut ctx, - &handler, - engines, - item, - false, - None, - Some(kind), - ) - .unwrap(); - - let decl = match nodes[0].content { - AstNodeContent::Declaration(Declaration::FunctionDeclaration(f)) => f, - _ => unreachable!("unexpected node"), - }; - - if handler.has_errors() { - panic!( - "{:?} {:?}", - handler, - program_id - .and_then(|x| engines.se().get_source_ids_from_program_id(x)) - .unwrap() - .iter() - .map(|x| engines.se().get_file_name(x)) - .collect::>() - ); - } - assert!(!handler.has_warnings(), "{:?}", handler); - - let mut ctx = self.ctx.by_ref(); - let _r = TyDecl::collect( - &handler, - engines, - ctx.collection_ctx, - Declaration::FunctionDeclaration(decl), - ); - if handler.has_errors() { - return Err(handler); - } - - let r = ctx.scoped(&handler, None, |ctx| { - TyDecl::type_check( - &handler, - &mut ctx.by_ref(), - parsed::Declaration::FunctionDeclaration(decl), - ) - }); - - // Uncomment this to understand why an entry function was not generated - // println!("{}, {:#?}", r.is_ok(), handler); - - let decl = r.map_err(|_| handler.clone())?; - - if handler.has_errors() || matches!(decl, TyDecl::ErrorRecovery(_, _)) { - Err(handler) - } else { - Ok(TyAstNode { - span: decl.span(engines), - content: ty::TyAstNodeContent::Declaration(decl), - }) - } - } - - fn parse_impl_trait_to_ty_ast_node( - &mut self, - engines: &Engines, - program_id: Option, - code: &str, - ) -> Result { - let mut ctx = crate::transform::to_parsed_lang::Context::new( - crate::BuildTarget::Fuel, - self.ctx.experimental, - ); - - let handler = Handler::default(); - - let item = Self::parse(engines, program_id, code); - let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes( - &mut ctx, &handler, engines, item, false, None, None, - ) - .unwrap(); - - let decl = match nodes[0].content { - AstNodeContent::Declaration(Declaration::ImplSelfOrTrait(f)) => f, - _ => unreachable!("unexpected item"), - }; - - assert!(!handler.has_errors(), "{:?}", handler); - - let mut ctx = self.ctx.by_ref(); - let _r = TyDecl::collect( - &handler, - engines, - ctx.collection_ctx, - Declaration::ImplSelfOrTrait(decl), - ); - if handler.has_errors() { - return Err(handler); - } - - let r = ctx.scoped(&handler, None, |ctx| { - TyDecl::type_check(&handler, ctx, Declaration::ImplSelfOrTrait(decl)) - }); - - // Uncomment this to understand why auto impl failed for a type. - // println!("{:#?}", handler); - - let decl = r.map_err(|_| handler.clone())?; - - if handler.has_errors() || matches!(decl, TyDecl::ErrorRecovery(_, _)) { - Err(handler) - } else { - let impl_trait = if let TyDecl::ImplSelfOrTrait(impl_trait_id) = &decl { - engines.de().get_impl_self_or_trait(&impl_trait_id.decl_id) - } else { - unreachable!(); - }; - - // Insert trait implementation generated in the previous scope into the current scope. - ctx.insert_trait_implementation( - &handler, - impl_trait.trait_name.clone(), - impl_trait.trait_type_arguments.clone(), - impl_trait.implementing_for.type_id, - &impl_trait.items, - &impl_trait.span, - impl_trait - .trait_decl_ref - .as_ref() - .map(|decl_ref| decl_ref.decl_span().clone()), - crate::namespace::IsImplSelf::No, - crate::namespace::IsExtendingExistingImpl::No, - ) - .ok(); - - Ok(TyAstNode { - span: decl.span(engines), - content: ty::TyAstNodeContent::Declaration(decl), - }) - } - } - // Auto implements AbiEncode and AbiDecode for structs and returns their `AstNode`s. fn auto_impl_struct( &mut self, @@ -505,7 +260,7 @@ where Some((abi_encode_node.ok(), abi_decode_node.ok())) } - pub fn generate( + pub fn generate_abi_encode_and_decode_impls( &mut self, engines: &Engines, decl: &ty::TyDecl, @@ -517,17 +272,6 @@ where } } - // The safest way would be to return a canonical fully qualified type path. - // We do not have a way to do this at the moment, so the best way is to use - // exactly what was typed by the user, to accommodate aliased imports. - fn generate_type(engines: &Engines, t: &TypeArgument) -> Option { - match &*engines.te().get(t.type_id) { - // when a function does not define a return type, the span points to the whole signature. - TypeInfo::Tuple(v) if v.is_empty() => Some("()".into()), - _ => Some(t.span().as_str().to_string()), - } - } - pub(crate) fn generate_contract_entry( &mut self, engines: &Engines, diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs new file mode 100644 index 00000000000..055836ebcf6 --- /dev/null +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs @@ -0,0 +1,56 @@ +use crate::{ + decl_engine::DeclEngineGet, + language::ty::{self, TyAstNode, TyDecl}, + Engines, +}; +use sway_error::handler::Handler; +use sway_types::{Named, Spanned}; + +#[derive(Default)] +pub struct MarkerTraitsAutoImplInfo {} + +pub type MarkerTraitsAutoImplContext<'a, 'b> = + super::AutoImplContext<'a, 'b, MarkerTraitsAutoImplInfo>; + +impl<'a, 'b> MarkerTraitsAutoImplContext<'a, 'b> +where + 'a: 'b, +{ + /// Generates and implementation of the `Enum` marker trait for the user defined enum + /// represented by the `enum_decl`. + pub fn generate_enum_marker_trait_impl( + &mut self, + engines: &Engines, + enum_decl: &ty::TyDecl, + ) -> Option { + match enum_decl { + TyDecl::EnumDecl(_) => self.auto_impl_enum_marker_trait(engines, enum_decl), + _ => None, + } + } + + fn auto_impl_enum_marker_trait( + &mut self, + engines: &Engines, + enum_decl: &TyDecl, + ) -> Option { + if self.ctx.namespace.current_module().is_core_marker_module() { + return None; + } + + let enum_decl_id = enum_decl.to_enum_id(&Handler::default(), engines).unwrap(); + let enum_decl = self.ctx.engines().de().get(&enum_decl_id); + + let program_id = enum_decl.span().source_id().map(|sid| sid.program_id()); + + let impl_enum_code = format!( + "#[allow(dead_code)] impl Enum for {} {{ }}", + enum_decl.name() + ); + + let impl_enum_node = + self.parse_impl_trait_to_ty_ast_node(engines, program_id, &impl_enum_code); + + impl_enum_node.ok() + } +} diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs new file mode 100644 index 00000000000..c0733092f86 --- /dev/null +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs @@ -0,0 +1,303 @@ +//! This module contains common infrastructure for generating and parsing auto-generated code. +pub mod abi_encoding; +pub mod marker_traits; + +use crate::{ + engine_threading::SpannedWithEngines, + language::{ + parsed::{self, AstNodeContent, Declaration, FunctionDeclarationKind}, + ty::{self, TyAstNode, TyDecl}, + }, + semantic_analysis::TypeCheckContext, + Engines, TypeArgument, TypeInfo, TypeParameter, +}; +use sway_error::handler::Handler; +use sway_parse::Parse; +use sway_types::{ProgramId, Spanned}; + +/// Contains all information needed to auto-implement code for a certain feature. +pub struct AutoImplContext<'a, 'b, I> +where + 'a: 'b, +{ + ctx: &'b mut TypeCheckContext<'a>, + /// Additional information, aside from `ctx`, needed to auto-implement a concrete feature. + #[allow(dead_code)] + info: I, +} + +impl<'a, 'b, I> AutoImplContext<'a, 'b, I> +where + 'a: 'b, +{ + pub fn new(ctx: &'b mut TypeCheckContext<'a>) -> Self + where + I: Default, + { + Self { + ctx, + info: I::default(), + } + } + + /// Parses `input` into the expected [Parse] type. + /// The resulted [Parse] has source id set to autogenerated source id + /// within the program represented by the `program_id`. + fn parse(engines: &Engines, program_id: Option, input: &str) -> T + where + T: Parse, + { + // Uncomment this to see what is being generated + // println!("{}", input); + + let handler = <_>::default(); + let source_id = + program_id.map(|program_id| engines.se().get_autogenerated_source_id(program_id)); + + let ts = sway_parse::lex( + &handler, + &std::sync::Arc::from(input), + 0, + input.len(), + source_id, + ) + .unwrap(); + let mut p = sway_parse::Parser::new(&handler, &ts); + p.check_double_underscore = false; + + let r = p.parse(); + + assert!(!handler.has_errors(), "{:?}", handler); + assert!(!handler.has_warnings(), "{:?}", handler); + + assert!(!p.has_errors()); + assert!(!p.has_warnings()); + + r.unwrap() + } + + /// Generates code like: ``. + fn generate_type_parameters_declaration_code( + &self, + type_parameters: &[TypeParameter], + ) -> String { + if type_parameters.is_empty() { + String::new() + } else { + format!( + "<{}>", + itertools::intersperse(type_parameters.iter().map(|x| { x.name.as_str() }), ", ") + .collect::() + ) + } + } + + /// Generates code like: `T: Eq + Hash,\n`. + fn generate_type_parameters_constraints_code( + &self, + type_parameters: &[TypeParameter], + extra_constraint: &str, + ) -> String { + let mut code = String::new(); + + for t in type_parameters.iter() { + code.push_str(&format!( + "{}: {},\n", + t.name.as_str(), + itertools::intersperse( + [extra_constraint].into_iter().chain( + t.trait_constraints + .iter() + .map(|x| x.trait_name.suffix.as_str()) + ), + " + " + ) + .collect::() + )); + } + + if !code.is_empty() { + code = format!(" where {code}\n"); + } + + code + } + + /// Parses `code` that contains [Declaration::FunctionDeclaration] into the + /// corresponding [TyAstNode]. + pub fn parse_fn_to_ty_ast_node( + &mut self, + engines: &Engines, + program_id: Option, + kind: FunctionDeclarationKind, + code: &str, + ) -> Result { + let mut ctx = crate::transform::to_parsed_lang::Context::new( + crate::BuildTarget::Fuel, + self.ctx.experimental, + ); + + let handler = Handler::default(); + + let item = Self::parse(engines, program_id, code); + let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes( + &mut ctx, + &handler, + engines, + item, + false, + None, + Some(kind), + ) + .unwrap(); + + let decl = match nodes[0].content { + AstNodeContent::Declaration(Declaration::FunctionDeclaration(f)) => f, + _ => unreachable!("unexpected node; expected `Declaration::FunctionDeclaration`"), + }; + + if handler.has_errors() { + panic!( + "{:?} {:?}", + handler, + program_id + .and_then(|x| engines.se().get_source_ids_from_program_id(x)) + .unwrap() + .iter() + .map(|x| engines.se().get_file_name(x)) + .collect::>() + ); + } + assert!(!handler.has_warnings(), "{:?}", handler); + + let mut ctx = self.ctx.by_ref(); + let _r = TyDecl::collect( + &handler, + engines, + ctx.collection_ctx, + Declaration::FunctionDeclaration(decl), + ); + if handler.has_errors() { + return Err(handler); + } + + let r = ctx.scoped(&handler, None, |ctx| { + TyDecl::type_check( + &handler, + &mut ctx.by_ref(), + parsed::Declaration::FunctionDeclaration(decl), + ) + }); + + // Uncomment this to understand why an entry function was not generated + // println!("{}, {:#?}", r.is_ok(), handler); + + let decl = r.map_err(|_| handler.clone())?; + + if handler.has_errors() || matches!(decl, TyDecl::ErrorRecovery(_, _)) { + Err(handler) + } else { + Ok(TyAstNode { + span: decl.span(engines), + content: ty::TyAstNodeContent::Declaration(decl), + }) + } + } + + /// Parses `code` that contains [Declaration::ImplSelfOrTrait] into the + /// corresponding [TyAstNode]. + fn parse_impl_trait_to_ty_ast_node( + &mut self, + engines: &Engines, + program_id: Option, + code: &str, + ) -> Result { + let mut ctx = crate::transform::to_parsed_lang::Context::new( + crate::BuildTarget::Fuel, + self.ctx.experimental, + ); + + let handler = Handler::default(); + + let item = Self::parse(engines, program_id, code); + let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes( + &mut ctx, &handler, engines, item, false, None, None, + ) + .unwrap(); + + let decl = match nodes[0].content { + AstNodeContent::Declaration(Declaration::ImplSelfOrTrait(f)) => f, + _ => unreachable!("unexpected node; expected `Declaration::ImplSelfOrTrait`"), + }; + + assert!(!handler.has_errors(), "{:?}", handler); + + let mut ctx = self.ctx.by_ref(); + let _r = TyDecl::collect( + &handler, + engines, + ctx.collection_ctx, + Declaration::ImplSelfOrTrait(decl), + ); + if handler.has_errors() { + return Err(handler); + } + + let r = ctx.scoped(&handler, None, |ctx| { + TyDecl::type_check(&handler, ctx, Declaration::ImplSelfOrTrait(decl)) + }); + + // Uncomment this to understand why auto impl failed for a type. + // println!("{:#?}", handler); + + let decl = r.map_err(|_| handler.clone())?; + + if handler.has_errors() || matches!(decl, TyDecl::ErrorRecovery(_, _)) { + Err(handler) + } else { + let impl_trait = if let TyDecl::ImplSelfOrTrait(impl_trait_id) = &decl { + engines.de().get_impl_self_or_trait(&impl_trait_id.decl_id) + } else { + unreachable!(); + }; + + // Insert trait implementation generated in the previous scope into the current scope. + ctx.insert_trait_implementation( + &handler, + impl_trait.trait_name.clone(), + impl_trait.trait_type_arguments.clone(), + impl_trait.implementing_for.type_id, + &impl_trait.items, + &impl_trait.span, + impl_trait + .trait_decl_ref + .as_ref() + .map(|decl_ref| decl_ref.decl_span().clone()), + crate::namespace::IsImplSelf::No, + crate::namespace::IsExtendingExistingImpl::No, + ) + .ok(); + + Ok(TyAstNode { + span: decl.span(engines), + content: ty::TyAstNodeContent::Declaration(decl), + }) + } + } + + /// Returns the string representation of the type given by `ta`, as given in code + /// by the `ta`'s span. + /// + /// The safest way would be to return a canonical fully qualified type path. + /// We do not have a way to do this at the moment, so the best way is to use + /// exactly what was typed by the user, to accommodate aliased imports. + fn generate_type(engines: &Engines, ta: &TypeArgument) -> Option { + match &*engines.te().get(ta.type_id) { + // A special case for function return type. + // When a function does not define a return type, the span points to the whole signature. + TypeInfo::Tuple(v) if v.is_empty() => Some("()".into()), + // Otherwise, take the type from the span. + _ => Some(ta.span().as_str().to_string()), + } + } +} diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs index e795c406299..644bfbb764d 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs @@ -75,6 +75,39 @@ impl TyImplSelfOrTrait { mut ctx: TypeCheckContext, impl_trait: ImplSelfOrTrait, ) -> Result { + // If the impl trait represents an attempt to explicit implement a marker trait + // we will emit an error, but do not want to bail out. We still want to check + // the particular explicit implementation for errors, which will be done in the + // remaining part of the type check. + // At the very end of the type checking, we again inspect the result of this check + // and return error if there was one. + let is_marker_trait_explicit_impl_result = handler.scope(|handler| { + let resolved_decl = ctx.resolve_call_path(handler, &impl_trait.trait_name)?; + let trait_decl = match &resolved_decl { + TyDecl::TraitDecl(decl) => ctx.engines.de().get_trait(&decl.decl_id), + // This should never be the case, but even if it happens, we just return `Ok` and continue + // with the type checking that anyhow needs to handle it. + _ => return Ok(()), + }; + + // Explicit impl means it is not autogenerated by the compiler and is not in the "core::marker" module. + if trait_decl.is_marker_trait() + && !(ctx.namespace.current_module().is_core_marker_module() + || impl_trait.is_autogenerated(ctx.engines.se())) + { + Err( + handler.emit_err(CompileError::MarkerTraitExplicitlyImplemented { + marker_trait_full_name: trait_decl + .call_path + .to_string_with_args(ctx.engines, &impl_trait.trait_type_arguments), + span: impl_trait.trait_name.span(), + }), + ) + } else { + Ok(()) + } + }); + let ImplSelfOrTrait { impl_type_parameters, trait_name, @@ -331,7 +364,13 @@ impl TyImplSelfOrTrait { })); } }; - Ok(impl_trait) + + // If there was no error on explicit implementation of a marker trait, + // return the type-checked trait. Otherwise, return the emitted error. + match is_marker_trait_explicit_impl_result { + Ok(_) => Ok(impl_trait), + Err(err) => Err(err), + } }) } diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 5da6552fef5..c92d2eb60fd 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -27,7 +27,9 @@ use crate::{ }; use super::{ - declaration::auto_impl::{self, EncodingAutoImplContext}, + declaration::auto_impl::{ + abi_encoding::AbiEncodingAutoImplContext, marker_traits::MarkerTraitsAutoImplContext, + }, symbol_collection_context::SymbolCollectionContext, }; @@ -393,8 +395,7 @@ impl ty::TyModule { match (&kind, main_decl.is_some()) { (TreeType::Predicate, true) => { - let mut fn_generator = - auto_impl::EncodingAutoImplContext::new(&mut ctx).unwrap(); + let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx); if let Ok(node) = fn_generator.generate_predicate_entry( engines, main_decl.as_ref().unwrap(), @@ -404,8 +405,7 @@ impl ty::TyModule { } } (TreeType::Script, true) => { - let mut fn_generator = - auto_impl::EncodingAutoImplContext::new(&mut ctx).unwrap(); + let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx); if let Ok(node) = fn_generator.generate_script_entry( engines, main_decl.as_ref().unwrap(), @@ -443,8 +443,7 @@ impl ty::TyModule { .all(|si| !PartialEqWithEngines::eq(method, si, &partialeq_ctx)) }); - let mut fn_generator = - auto_impl::EncodingAutoImplContext::new(&mut ctx).unwrap(); + let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx); if let Ok(node) = fn_generator.generate_contract_entry( engines, parsed.span.source_id().map(|x| x.program_id()), @@ -496,9 +495,6 @@ impl ty::TyModule { predicate: fn(&ImplSelfOrTrait) -> bool, ) -> HashMap> { let engines = ctx.engines(); - // Check which structs and enums needs to have auto impl for AbiEncode - // We need to do this before type checking, because the impls must be right after - // the declarations let mut impls = HashMap::>::new(); for node in nodes.iter() { @@ -539,12 +535,17 @@ impl ty::TyModule { nodes: &[AstNode], ) -> Result, ErrorEmitted> { let engines = ctx.engines(); + + // Check which structs and enums needs to have auto impl for `AbiEncode` and `AbiDecode`. + // We need to do this before type checking, because the impls must be right after + // the declarations. let all_abiencode_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| { decl.trait_name.suffix.as_str() == "AbiEncode" }); let mut typed_nodes = vec![]; for node in nodes { + // Check if the encoding traits are explicitly implemented. let auto_impl_encoding_traits = match &node.content { AstNodeContent::Declaration(Declaration::StructDeclaration(decl_id)) => { let decl = ctx.engines().pe().get_struct(decl_id); @@ -561,28 +562,37 @@ impl ty::TyModule { continue; }; + // Auto impl encoding traits only if they are not explicitly implemented. + let mut generated = vec![]; if ctx.experimental.new_encoding { - let mut generated = vec![]; - if let (true, Some(mut ctx)) = ( + if let (true, mut ctx) = ( auto_impl_encoding_traits, - EncodingAutoImplContext::new(&mut ctx), + AbiEncodingAutoImplContext::new(&mut ctx), ) { match &node.content { TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_)) | TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => { - let (a, b) = ctx.generate(engines, decl); + let (a, b) = ctx.generate_abi_encode_and_decode_impls(engines, decl); generated.extend(a); generated.extend(b); } _ => {} } }; + } - typed_nodes.push(node); - typed_nodes.extend(generated); - } else { - typed_nodes.push(node); + // Always auto impl marker traits. If an explicit implementation exists, that will be + // reported as an error when type-checking trait impls. + if ctx.experimental.error_type { + let mut ctx = MarkerTraitsAutoImplContext::new(&mut ctx); + if let TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) = &node.content { + let a = ctx.generate_enum_marker_trait_impl(engines, decl); + generated.extend(a); + } } + + typed_nodes.push(node); + typed_nodes.extend(generated); } Ok(typed_nodes) diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index a732c0764fa..885537f52e4 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -354,24 +354,8 @@ impl TraitMap { && traits_are_subset && matches!(is_impl_self, IsImplSelf::No) { - let trait_name_str = format!( - "{}{}", - trait_name, - if trait_type_args.is_empty() { - String::new() - } else { - format!( - "<{}>", - trait_type_args - .iter() - .map(|type_arg| engines.help_out(type_arg).to_string()) - .collect::>() - .join(", ") - ) - } - ); handler.emit_err(CompileError::ConflictingImplsForTraitAndType { - trait_name: trait_name_str, + trait_name: trait_name.to_string_with_args(engines, &trait_type_args), type_implementing_for: engines.help_out(type_id).to_string(), existing_impl_span: existing_impl_span.clone(), second_impl_span: impl_span.clone(), diff --git a/sway-core/src/type_system/ast_elements/type_parameter.rs b/sway-core/src/type_system/ast_elements/type_parameter.rs index d6ab976517a..63c485419d0 100644 --- a/sway-core/src/type_system/ast_elements/type_parameter.rs +++ b/sway-core/src/type_system/ast_elements/type_parameter.rs @@ -681,6 +681,8 @@ fn handle_trait( .iter() .map(|trait_decl| { // In the case of an internal library, always add :: to the candidate call path. + // TODO: Replace with a call to a dedicated `CallPath` method + // once https://github.com/FuelLabs/sway/issues/6873 is fixed. let full_path = trait_decl .call_path .to_fullpath(ctx.engines(), ctx.namespace()); diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 04e3473d5b1..3d5c457a0cc 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -741,7 +741,7 @@ impl<'a> UnifyCheck<'a> { assert!( matches!(left.call_path.callpath_type, CallPathType::Full) && matches!(right.call_path.callpath_type, CallPathType::Full), - "The call paths of the enum declarations must always be resolved." + "call paths of enum declarations must always be full paths" ); // Avoid unnecessary `collect::>` of variant names @@ -802,7 +802,7 @@ impl<'a> UnifyCheck<'a> { assert!( matches!(left.call_path.callpath_type, CallPathType::Full) && matches!(right.call_path.callpath_type, CallPathType::Full), - "The call paths of the enum declarations must always be resolved." + "call paths of struct declarations must always be full paths" ); // Avoid unnecessary `collect::>` of variant names diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index b28fa5b2210..3100ce8e76c 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -616,6 +616,13 @@ pub enum CompileError { existing_impl_span: Span, second_impl_span: Span, }, + #[error( + "\"{marker_trait_full_name}\" is a marker trait and cannot be explicitly implemented." + )] + MarkerTraitExplicitlyImplemented { + marker_trait_full_name: String, + span: Span, + }, #[error("Duplicate definitions for the {decl_kind} \"{decl_name}\" for type \"{type_implementing_for}\".")] DuplicateDeclDefinedForType { decl_kind: String, @@ -1157,6 +1164,7 @@ impl Spanned for CompileError { ConflictingImplsForTraitAndType { second_impl_span, .. } => second_impl_span.clone(), + MarkerTraitExplicitlyImplemented { span, .. } => span.clone(), DuplicateDeclDefinedForType { span, .. } => span.clone(), IncorrectNumberOfInterfaceSurfaceFunctionParameters { span, .. } => span.clone(), ArgumentParameterTypeMismatch { span, .. } => span.clone(), @@ -2167,14 +2175,14 @@ impl ToDiagnostic for CompileError { } }, SymbolWithMultipleBindings { name, paths, span } => Diagnostic { - reason: Some(Reason::new(code(1), "Multiple bindings for symbol in this scope".to_string())), + reason: Some(Reason::new(code(1), "Multiple bindings exist for symbol in the scope".to_string())), issue: Issue::error( source_engine, span.clone(), - format!("The following paths are all valid bindings for symbol \"{}\": {}", name, paths.iter().map(|path| format!("{path}::{name}")).collect::>().join(", ")), + format!("The following paths are all valid bindings for symbol \"{}\": {}.", name, sequence_to_str(&paths.iter().map(|path| format!("{path}::{name}")).collect::>(), Enclosing::DoubleQuote, 2)), ), - hints: paths.iter().map(|path| Hint::info(source_engine, Span::dummy(), format!("{path}::{}", name.as_str()))).collect(), - help: vec![format!("Consider using a fully qualified name, e.g., {}::{}", paths[0], name.as_str())], + hints: vec![], + help: vec![format!("Consider using a fully qualified name, e.g., `{}::{}`.", paths[0], name)], }, StorageFieldDoesNotExist { field_name, available_fields, storage_decl_span } => Diagnostic { reason: Some(Reason::new(code(1), "Storage field does not exist".to_string())), @@ -2315,6 +2323,25 @@ impl ToDiagnostic for CompileError { "This property is called \"trait coherence\".".to_string(), ], }, + MarkerTraitExplicitlyImplemented { marker_trait_full_name, span} => Diagnostic { + reason: Some(Reason::new(code(1), "Marker traits cannot be explicitly implemented".to_string())), + issue: Issue::error( + source_engine, + span.clone(), + format!("Trait \"{marker_trait_full_name}\" is a marker trait and cannot be explicitly implemented.") + ), + hints: vec![], + help: match marker_trait_name(marker_trait_full_name) { + "Error" => vec![ + "\"Error\" marker trait is automatically implemented by the compiler for string slices".to_string(), + "and enums annotated with the `#[error_type]` attribute.".to_string(), + ], + "Enum" => vec![ + "\"Enum\" marker trait is automatically implemented by the compiler for all enum types.".to_string(), + ], + _ => vec![], + } + }, AssignmentToNonMutableVariable { lhs_span, decl_name } => Diagnostic { reason: Some(Reason::new(code(1), "Immutable variables cannot be assigned to".to_string())), issue: Issue::error( @@ -2951,3 +2978,31 @@ impl fmt::Display for StorageAccess { } } } + +/// Extracts only the suffix part of the `marker_trait_full_name`, without the arguments. +/// E.g.: +/// - `core::marker::Error` => `Error` +/// - `core::marker::SomeMarkerTrait::` => `SomeMarkerTrait` +/// - `core::marker::SomeMarkerTrait` => `SomeMarkerTrait` +/// +/// Panics if the `marker_trait_full_name` does not start with "core::marker::". +fn marker_trait_name(marker_trait_full_name: &str) -> &str { + const MARKER_TRAITS_MODULE: &str = "core::marker::"; + assert!( + marker_trait_full_name.starts_with(MARKER_TRAITS_MODULE), + "`marker_trait_full_name` must start with \"core::marker::\", but it was \"{}\"", + marker_trait_full_name + ); + + let lower_boundary = MARKER_TRAITS_MODULE.len(); + let name_part = &marker_trait_full_name[lower_boundary..]; + + let upper_boundary = marker_trait_full_name.len() - lower_boundary; + let only_name_len = std::cmp::min( + name_part.find(':').unwrap_or(upper_boundary), + name_part.find('<').unwrap_or(upper_boundary), + ); + let upper_boundary = lower_boundary + only_name_len; + + &marker_trait_full_name[lower_boundary..upper_boundary] +} diff --git a/sway-features/src/lib.rs b/sway-features/src/lib.rs index 953827a30e5..9916366765a 100644 --- a/sway-features/src/lib.rs +++ b/sway-features/src/lib.rs @@ -157,6 +157,8 @@ features! { "https://github.com/FuelLabs/sway/issues/6701", references = true, "https://github.com/FuelLabs/sway/issues/5063", + error_type = false, + "https://github.com/FuelLabs/sway/issues/6765", } #[derive(Clone, Debug, Default, Parser)] diff --git a/sway-lib-core/src/lib.sw b/sway-lib-core/src/lib.sw index 4437bdd85cb..8fce298b922 100644 --- a/sway-lib-core/src/lib.sw +++ b/sway-lib-core/src/lib.sw @@ -11,3 +11,4 @@ pub mod never; pub mod r#storage; pub mod prelude; pub mod codec; +pub mod marker; diff --git a/sway-lib-core/src/marker.sw b/sway-lib-core/src/marker.sw new file mode 100644 index 00000000000..d6e29c4ccb5 --- /dev/null +++ b/sway-lib-core/src/marker.sw @@ -0,0 +1,29 @@ +//! Marker traits that represent certain properties of types. +//! +//! Sway types can be classified in various ways according to their intrinsic properties. +//! These classifications are represented as marker traits. Marker traits are implemented +//! by the compiler and cannot be explicitly implemented in code. +library; + +/// A marker for error types. +/// +/// Error types are types whose instances can be arguments to the `panic` instruction. +/// +/// [Error] is automatically implemented for: +/// - string slices, +/// - and enums annotated with the `#[error_type]` attribute. +#[cfg(experimental_error_type = true)] +pub trait Error { +} + +/// A marker for enum types. +#[cfg(experimental_error_type = true)] +pub trait Enum { +} + +// Marker traits cannot be explicitly implement in code, except in this module. +// If a marker trait needs to be implemented for a built-in type, those implementation +// will be provided here. + +#[cfg(experimental_error_type = true)] +impl Error for str {} diff --git a/sway-lib-core/src/prelude.sw b/sway-lib-core/src/prelude.sw index cdec0239241..053a2d0fbc8 100644 --- a/sway-lib-core/src/prelude.sw +++ b/sway-lib-core/src/prelude.sw @@ -12,3 +12,5 @@ pub use ::ops::*; pub use ::storage::*; pub use ::str::*; pub use ::codec::*; +#[cfg(experimental_error_type = true)] +pub use ::marker::*; diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap index 993f90f8407..7e6f8283aae 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap @@ -11,7 +11,7 @@ warning | 1 | predicate; 2 | #[cfg(c)] a - | --- Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" or "experimental_storage_domains" or "experimental_references" + | --- Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" or "experimental_storage_domains" or "experimental_references" or "experimental_error_type" | ____ diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/import_star_name_clash/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/language/import_star_name_clash/test.toml index 0fad6830333..1fcf5a0d931 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/language/import_star_name_clash/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/import_star_name_clash/test.toml @@ -4,22 +4,22 @@ category = "fail" # check: $()Could not find symbol "A" in this scope. # check: $()error -# check: $()The following paths are all valid bindings for symbol "MyEnum": a::MyEnum, b::MyEnum +# check: $()The following paths are all valid bindings for symbol "MyEnum": "a::MyEnum" and "b::MyEnum". # check: $()error # check: $()Unknown type name "MyEnum". # check: $()error -# check: $()The following paths are all valid bindings for symbol "E": c::MyEnumVariants::E, d::MyOtherEnumVariants::E +# check: $()The following paths are all valid bindings for symbol "E": "c::MyEnumVariants::E" and "d::MyOtherEnumVariants::E". # check: $()error -# check: $()The following paths are all valid bindings for symbol "MyStruct": a::MyStruct, b::MyStruct +# check: $()The following paths are all valid bindings for symbol "MyStruct": "a::MyStruct" and "b::MyStruct". # check: $()error # check: $()Unknown type name "MyStruct". # check: $()error -# check: $()The following paths are all valid bindings for symbol "MyStruct": a::MyStruct, b::MyStruct +# check: $()The following paths are all valid bindings for symbol "MyStruct": "a::MyStruct" and "b::MyStruct". # check: $()error # check: $()Unknown type name "MyStruct". @@ -34,7 +34,7 @@ category = "fail" # check: $()Could not find symbol "A" in this scope. # check: $()error -# check: $()The following paths are all valid bindings for symbol "MyEnum": a::MyEnum, b::MyEnum +# check: $()The following paths are all valid bindings for symbol "MyEnum": "a::MyEnum" and "b::MyEnum". # check: $()error # check: $()Unknown type name "MyEnum". @@ -43,7 +43,7 @@ category = "fail" # check: $()Could not find symbol "B" in this scope. # check: $()error -# check: $()The following paths are all valid bindings for symbol "MyEnum": a::MyEnum, b::MyEnum +# check: $()The following paths are all valid bindings for symbol "MyEnum": "a::MyEnum" and "b::MyEnum". # check: $()error # check: $()Unknown type name "MyEnum". @@ -57,16 +57,16 @@ category = "fail" # nextln: $()found: MyEnum. # check: $()error -# check: $()The following paths are all valid bindings for symbol "C": a::C, b::MyOtherEnum::C +# check: $()The following paths are all valid bindings for symbol "C": "a::C" and "b::MyOtherEnum::C". # check: $()error # check: $()Unknown type name "C". # check: $()error -# check: $()The following paths are all valid bindings for symbol "C": a::C, b::MyOtherEnum::C +# check: $()The following paths are all valid bindings for symbol "C": "a::C" and "b::MyOtherEnum::C" # check: $()error -# check: $()The following paths are all valid bindings for symbol "E": c::MyEnumVariants::E, d::MyOtherEnumVariants::E +# check: $()The following paths are all valid bindings for symbol "E": "c::MyEnumVariants::E" and "d::MyOtherEnumVariants::E". # check: $()error # check: $()Mismatched types. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/Forc.lock new file mode 100644 index 00000000000..9197cfc18c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-8AFA071721F70D50" + +[[package]] +name = "marker_trait_cannot_be_explicitly_implemented" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/Forc.toml new file mode 100644 index 00000000000..7f06c593a64 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "marker_trait_cannot_be_explicitly_implemented" +entry = "main.sw" +implicit-std = false + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/src/main.sw new file mode 100644 index 00000000000..b9efdf9a86a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/src/main.sw @@ -0,0 +1,43 @@ +library; + +struct EmptyStruct { } + +struct Struct { + x: u8, +} + +enum EmptyEnum {} + +enum MyEnum { + A: (), +} + +// Implement `Enum`. +impl Enum for MyEnum { } + +impl Enum for EmptyEnum { } + +impl Enum for Struct { } + +impl Enum for EmptyStruct { + fn non_existing() {} +} + +impl Enum for [u64;0] { } + +impl core::marker::Enum for (u8, u16, u32, u64, u256) { } + +// Implement `Error`. +impl Error for MyEnum { } + +impl Error for EmptyEnum { } + +impl Error for Struct { } + +impl Error for EmptyStruct { + fn non_existing() {} +} + +impl Error for [u64;0] { } + +impl core::marker::Error for (u8, u16, u32, u64, u256) { } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/test.toml new file mode 100644 index 00000000000..6127ca8ff17 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_cannot_be_explicitly_implemented/test.toml @@ -0,0 +1,74 @@ +category = "fail" +experimental = { error_type = true } + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Enum for MyEnum { } +#nextln: $()Trait "core::marker::Enum" is a marker trait and cannot be explicitly implemented. +#check: $()"Enum" marker trait is automatically implemented by the compiler for all enum types. + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Enum for EmptyEnum { } +#nextln: $()Trait "core::marker::Enum" is a marker trait and cannot be explicitly implemented. +#check: $()"Enum" marker trait is automatically implemented by the compiler for all enum types. + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Enum for Struct { } +#nextln: $()Trait "core::marker::Enum" is a marker trait and cannot be explicitly implemented. +#check: $()"Enum" marker trait is automatically implemented by the compiler for all enum types. + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Enum for EmptyStruct { +#nextln: $()Trait "core::marker::Enum" is a marker trait and cannot be explicitly implemented. +#check: $()"Enum" marker trait is automatically implemented by the compiler for all enum types. + +#check: $()error +#check: $()impl Enum for EmptyStruct { +#nextln: $()fn non_existing() {} +#nextln: $()Function "non_existing" is not a part of trait "Enum"'s interface surface. + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl core::marker::Enum for (u8, u16, u32, u64, u256) { } +#nextln: $()Trait "core::marker::Enum" is a marker trait and cannot be explicitly implemented. +#check: $()"Enum" marker trait is automatically implemented by the compiler for all enum types. + +#--------------- + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Error for MyEnum { } +#nextln: $()Trait "core::marker::Error" is a marker trait and cannot be explicitly implemented. +#check: $()"Error" marker trait is automatically implemented by the compiler for string slices + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Error for EmptyEnum { } +#nextln: $()Trait "core::marker::Error" is a marker trait and cannot be explicitly implemented. +#check: $()"Error" marker trait is automatically implemented by the compiler for string slices + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Error for Struct { } +#nextln: $()Trait "core::marker::Error" is a marker trait and cannot be explicitly implemented. +#check: $()"Error" marker trait is automatically implemented by the compiler for string slices + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl Error for EmptyStruct { +#nextln: $()Trait "core::marker::Error" is a marker trait and cannot be explicitly implemented. +#check: $()"Error" marker trait is automatically implemented by the compiler for string slices + +#check: $()error +#check: $()impl Error for EmptyStruct { +#nextln: $()fn non_existing() {} +#nextln: $()Function "non_existing" is not a part of trait "Error"'s interface surface. + +#check: $()error +#sameln: $()Marker traits cannot be explicitly implemented +#check: $()impl core::marker::Error for (u8, u16, u32, u64, u256) { } +#nextln: $()Trait "core::marker::Error" is a marker trait and cannot be explicitly implemented. +#check: $()"Error" marker trait is automatically implemented by the compiler for string slices diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/Forc.lock new file mode 100644 index 00000000000..b0009e3c924 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-A09B728E44881C03" + +[[package]] +name = "marker_trait_enum_not_implemented_for_non_enum" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/Forc.toml new file mode 100644 index 00000000000..a137b04e13f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "marker_trait_enum_not_implemented_for_non_enum" +entry = "main.sw" +implicit-std = false + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/src/main.sw new file mode 100644 index 00000000000..49e71186ce8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/src/main.sw @@ -0,0 +1,25 @@ +library; + +struct EmptyStruct { } + +struct Struct { + x: u8, +} + +fn implements_enum(_t: T) where T: Enum { } +fn implements_enum_no_args() where T: Enum { } + +pub fn main() { + implements_enum(0u64); + implements_enum_no_args::(); + implements_enum(Struct { x: 0 }); + implements_enum_no_args::(); + implements_enum(EmptyStruct { }); + implements_enum_no_args::(); + implements_enum([0u8, 0, 0]); + implements_enum_no_args::<[u8;3]>(); + implements_enum((0, 0, 0)); + implements_enum_no_args::<(u64,u64,u64)>(); + implements_enum(()); + implements_enum_no_args::<()>(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/test.toml new file mode 100644 index 00000000000..90a9d7c8e4d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_enum_not_implemented_for_non_enum/test.toml @@ -0,0 +1,50 @@ +category = "fail" +experimental = { error_type = true } + +#check: $()error +#check: $()implements_enum(0u64); +#nextln: $()Trait "Enum" is not implemented for type "u64". + +#check: $()error +#check: $()implements_enum_no_args::(); +#nextln: $()Trait "Enum" is not implemented for type "u64". + +#check: $()error +#check: $()implements_enum(Struct { x: 0 }); +#nextln: $()Trait "Enum" is not implemented for type "Struct". + +#check: $()error +#check: $()implements_enum_no_args::(); +#nextln: $()Trait "Enum" is not implemented for type "Struct". + +#check: $()error +#check: $()implements_enum(EmptyStruct { }); +#nextln: $()Trait "Enum" is not implemented for type "EmptyStruct". + +#check: $()error +#check: $()implements_enum_no_args::(); +#nextln: $()Trait "Enum" is not implemented for type "EmptyStruct". + +#check: $()error +#check: $()implements_enum([0u8, 0, 0]); +#nextln: $()Trait "Enum" is not implemented for type "[u8; 3]". + +#check: $()error +#check: $()implements_enum_no_args::<[u8;3]>(); +#nextln: $()Trait "Enum" is not implemented for type "[u8; 3]". + +#check: $()error +#check: $()implements_enum((0, 0, 0)); +#nextln: $()Trait "Enum" is not implemented for type "(u64, u64, u64)". + +#check: $()error +#check: $()implements_enum_no_args::<(u64,u64,u64)>(); +#nextln: $()Trait "Enum" is not implemented for type "(u64, u64, u64)". + +#check: $()error +#check: $()implements_enum(()); +#nextln: $()Trait "Enum" is not implemented for type "()". + +#check: $()error +#check: $()implements_enum_no_args::<()>(); +#nextln: $()Trait "Enum" is not implemented for type "()". diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/Forc.lock new file mode 100644 index 00000000000..77b44abe9ac --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-52BE33D40E20FF91" + +[[package]] +name = "marker_trait_error_not_implemented_for_non_error_types" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/Forc.toml new file mode 100644 index 00000000000..ff2f589e2f7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "marker_trait_error_not_implemented_for_non_error_types" +entry = "main.sw" +implicit-std = false + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/src/main.sw new file mode 100644 index 00000000000..e265a947fe1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/src/main.sw @@ -0,0 +1,25 @@ +library; + +struct EmptyStruct { } + +struct Struct { + x: u8, +} + +fn implements_error(_t: T) where T: Error { } +fn implements_error_no_args() where T: Error { } + +pub fn main() { + implements_error(0u64); + implements_error_no_args::(); + implements_error(Struct { x: 0 }); + implements_error_no_args::(); + implements_error(EmptyStruct { }); + implements_error_no_args::(); + implements_error([0u8, 0, 0]); + implements_error_no_args::<[u8;3]>(); + implements_error((0, 0, 0)); + implements_error_no_args::<(u64,u64,u64)>(); + implements_error(()); + implements_error_no_args::<()>(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/test.toml new file mode 100644 index 00000000000..6a64000826e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/marker_trait_error_not_implemented_for_non_error_types/test.toml @@ -0,0 +1,50 @@ +category = "fail" +experimental = { error_type = true } + +#check: $()error +#check: $()implements_error(0u64); +#nextln: $()Trait "Error" is not implemented for type "u64". + +#check: $()error +#check: $()implements_error_no_args::(); +#nextln: $()Trait "Error" is not implemented for type "u64". + +#check: $()error +#check: $()implements_error(Struct { x: 0 }); +#nextln: $()Trait "Error" is not implemented for type "Struct". + +#check: $()error +#check: $()implements_error_no_args::(); +#nextln: $()Trait "Error" is not implemented for type "Struct". + +#check: $()error +#check: $()implements_error(EmptyStruct { }); +#nextln: $()Trait "Error" is not implemented for type "EmptyStruct". + +#check: $()error +#check: $()implements_error_no_args::(); +#nextln: $()Trait "Error" is not implemented for type "EmptyStruct". + +#check: $()error +#check: $()implements_error([0u8, 0, 0]); +#nextln: $()Trait "Error" is not implemented for type "[u8; 3]". + +#check: $()error +#check: $()implements_error_no_args::<[u8;3]>(); +#nextln: $()Trait "Error" is not implemented for type "[u8; 3]". + +#check: $()error +#check: $()implements_error((0, 0, 0)); +#nextln: $()Trait "Error" is not implemented for type "(u64, u64, u64)". + +#check: $()error +#check: $()implements_error_no_args::<(u64,u64,u64)>(); +#nextln: $()Trait "Error" is not implemented for type "(u64, u64, u64)". + +#check: $()error +#check: $()implements_error(()); +#nextln: $()Trait "Error" is not implemented for type "()". + +#check: $()error +#check: $()implements_error_no_args::<()>(); +#nextln: $()Trait "Error" is not implemented for type "()". diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/Forc.lock new file mode 100644 index 00000000000..395f9c7ca06 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-4A42601F35BBA179" + +[[package]] +name = "marker_trait_enum_implemented_for_all_enums" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/Forc.toml new file mode 100644 index 00000000000..ea3edab1808 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "marker_trait_enum_implemented_for_all_enums" +entry = "main.sw" + +[dependencies] +core = { path = "../../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/enums_01.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/enums_01.sw new file mode 100644 index 00000000000..8fc1e3d4f2f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/enums_01.sw @@ -0,0 +1,7 @@ +library; + +pub enum LibEnum01 { + A: () +} + +pub enum EmptyLibEnum01 { } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/enums_02.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/enums_02.sw new file mode 100644 index 00000000000..f7ddf359b5e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/enums_02.sw @@ -0,0 +1,7 @@ +library; + +pub enum LibEnum02 { + A: () +} + +pub enum EmptyLibEnum02 { } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/main.sw new file mode 100644 index 00000000000..3e2078abe8e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/main.sw @@ -0,0 +1,45 @@ +library; + +pub mod enums_01; +pub mod enums_02; +mod use_error_explicitly; +mod use_error_via_glob; + +use enums_01::{LibEnum01, EmptyLibEnum01}; + +// TODO: Currently, trait impls will be imported into namespace +// only if the implemented for type is imported and they +// reside in the same module, or if glob imported. +// +// In the test, all `enums_02` enums are accessed using the +// full path. Remove this `use` once trait coherence and +// collecting of trait impls is implemented: +// https://github.com/FuelLabs/sway/issues/5892 +use enums_02::*; + +enum MainEnum { + A: (), +} + +enum MainEmptyEnum { } + +// Using `Enum` from core library prelude. +fn implements_enum(_t: T) where T: Enum { } +fn implements_enum_no_args() where T: Enum { } + +pub fn main() { + implements_enum(MainEnum::A); + implements_enum_no_args::(); + implements_enum_no_args::(); + + implements_enum(LibEnum01::A); + implements_enum_no_args::(); + implements_enum_no_args::(); + + implements_enum(enums_02::LibEnum02::A); + implements_enum_no_args::(); + implements_enum_no_args::(); + + use_error_explicitly::test(); + use_error_via_glob::test(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/use_error_explicitly.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/use_error_explicitly.sw new file mode 100644 index 00000000000..6389fd4cd68 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/use_error_explicitly.sw @@ -0,0 +1,32 @@ +library; + +use core::marker::Enum; + +use ::enums_01::{LibEnum01, EmptyLibEnum01}; + +// TODO: Remove one trait coherence and collecting of trait +// impls is implemented. See comment in `main.sw`. +use ::enums_02::*; + +enum LocalEnum { + A: (), +} + +enum LocalEmptyEnum { } + +fn implements_enum(_t: T) where T: Enum { } +fn implements_enum_no_args() where T: Enum { } + +pub fn test() { + implements_enum(LocalEnum::A); + implements_enum_no_args::(); + implements_enum_no_args::(); + + implements_enum(LibEnum01::A); + implements_enum_no_args::(); + implements_enum_no_args::(); + + implements_enum(::enums_02::LibEnum02::A); + implements_enum_no_args::<::enums_02::LibEnum02>(); + implements_enum_no_args::<::enums_02::EmptyLibEnum02>(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/use_error_via_glob.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/use_error_via_glob.sw new file mode 100644 index 00000000000..13359c0c314 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/src/use_error_via_glob.sw @@ -0,0 +1,32 @@ +library; + +use core::marker::*; + +use ::enums_01::{LibEnum01, EmptyLibEnum01}; + +// TODO: Remove one trait coherence and collecting of trait +// impls is implemented. See comment in `main.sw`. +use ::enums_02::*; + +enum LocalEnum { + A: (), +} + +enum LocalEmptyEnum { } + +fn implements_enum(_t: T) where T: Enum { } +fn implements_enum_no_args() where T: Enum { } + +pub fn test() { + implements_enum(LocalEnum::A); + implements_enum_no_args::(); + implements_enum_no_args::(); + + implements_enum(LibEnum01::A); + implements_enum_no_args::(); + implements_enum_no_args::(); + + implements_enum(::enums_02::LibEnum02::A); + implements_enum_no_args::<::enums_02::LibEnum02>(); + implements_enum_no_args::<::enums_02::EmptyLibEnum02>(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/test.toml new file mode 100644 index 00000000000..3ad0f0683d7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_enum_implemented_for_all_enums/test.toml @@ -0,0 +1,2 @@ +category = "compile" +experimental = { error_type = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/Forc.lock new file mode 100644 index 00000000000..5df74ef8251 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-831208117855D4CC" + +[[package]] +name = "marker_trait_error_implemented_for_str" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/Forc.toml new file mode 100644 index 00000000000..01c4e1812ee --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "marker_trait_error_implemented_for_str" +entry = "main.sw" + +[dependencies] +core = { path = "../../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/main.sw new file mode 100644 index 00000000000..d19949eb5dc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/main.sw @@ -0,0 +1,15 @@ +library; + +mod use_error_explicitly; +mod use_error_via_glob; + +// Using `Error` from core library prelude. +fn implements_error(_t: T) where T: Error { } +fn implements_error_no_args() where T: Error { } + +pub fn main() { + implements_error("str"); + implements_error_no_args::(); + use_error_explicitly::test(); + use_error_via_glob::test(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/use_error_explicitly.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/use_error_explicitly.sw new file mode 100644 index 00000000000..29bc2a3e79b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/use_error_explicitly.sw @@ -0,0 +1,11 @@ +library; + +use core::marker::Error; + +fn implements_error(_t: T) where T: Error { } +fn implements_error_no_args() where T: Error { } + +pub fn test() { + implements_error("str"); + implements_error_no_args::(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/use_error_via_glob.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/use_error_via_glob.sw new file mode 100644 index 00000000000..129cc06dcde --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/src/use_error_via_glob.sw @@ -0,0 +1,11 @@ +library; + +use core::marker::*; + +fn implements_error(_t: T) where T: Error { } +fn implements_error_no_args() where T: Error { } + +pub fn test() { + implements_error("str"); + implements_error_no_args::(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/test.toml new file mode 100644 index 00000000000..3ad0f0683d7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/marker_traits/marker_trait_error_implemented_for_str/test.toml @@ -0,0 +1,2 @@ +category = "compile" +experimental = { error_type = true } diff --git a/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/main.sw b/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/main.sw index 81e8986f3b1..07dcfda5f91 100644 --- a/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/main.sw +++ b/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/main.sw @@ -3,6 +3,7 @@ contract; mod impls; use impls::*; +use impls::Enum; use std::hash::{Hash, sha256}; use std::storage::storage_vec::*;