diff --git a/sway-core/src/convert_parse_tree.rs b/sway-core/src/convert_parse_tree.rs index b493db185cd..d7446b447a2 100644 --- a/sway-core/src/convert_parse_tree.rs +++ b/sway-core/src/convert_parse_tree.rs @@ -29,24 +29,30 @@ use { }; #[derive(Debug)] +/// Contains any errors or warnings that were generated during the conversion into the parse tree. +/// Typically these warnings and errors are populated as a side effect in the `From` and `Into` +/// implementations of error types into [ErrorEmitted]. pub struct ErrorContext { warnings: Vec, errors: Vec, } +#[derive(Debug)] +/// Represents that an error was emitted to the error context. This struct does not contain the +/// error, rather, other errors are responsible for pushing to the [ErrorContext] in their `Into` +/// implementations. pub struct ErrorEmitted { _priv: (), } impl ErrorContext { - /* + #[allow(dead_code)] pub fn warning(&mut self, warning: W) where W: Into, { self.warnings.push(warning.into()); } - */ pub fn error(&mut self, error: E) -> ErrorEmitted where @@ -157,7 +163,7 @@ pub enum ConvertParseTreeError { ConstructorPatternSubPatterns { span: Span }, #[error("paths are not supported in this position")] PathsNotSupportedHere { span: Span }, - #[error("fully specified types are not supported")] + #[error("Fully specified types are not supported in this position. Try importing the type and referring to it here.")] FullySpecifiedTypesNotSupported { span: Span }, #[error("ContractCaller requires exactly one generic argument")] ContractCallerOneGenericArg { span: Span }, @@ -926,20 +932,23 @@ fn expr_to_expression(ec: &mut ErrorContext, expr: Expr) -> Result Expression::StructExpression { - struct_name: path_expr_to_call_path(ec, path)?, - fields: { - fields - .into_inner() - .into_iter() - .map(|expr_struct_field| { - expr_struct_field_to_struct_expression_field(ec, expr_struct_field) - }) - .collect::>()? - }, - type_arguments: Vec::new(), - span, - }, + Expr::Struct { path, fields } => { + let (struct_name, type_arguments) = path_expr_to_call_path_type_args(ec, path)?; + Expression::StructExpression { + struct_name, + fields: { + fields + .into_inner() + .into_iter() + .map(|expr_struct_field| { + expr_struct_field_to_struct_expression_field(ec, expr_struct_field) + }) + .collect::>()? + }, + type_arguments, + span, + } + } Expr::Tuple(parenthesized_expr_tuple_descriptor) => Expression::Tuple { fields: expr_tuple_descriptor_to_expressions( ec, @@ -1660,6 +1669,30 @@ fn path_type_segment_to_ident( Ok(name) } +/// Similar to [path_type_segment_to_ident], but allows for the item to be either +/// type arguments _or_ an ident. +fn path_expr_segment_to_ident_or_type_argument( + ec: &mut ErrorContext, + path_expr_segment: PathExprSegment, +) -> Result<(Ident, Vec), ErrorEmitted> { + let PathExprSegment { + fully_qualified, + name, + generics_opt, + } = path_expr_segment; + if let Some(tilde_token) = fully_qualified { + let error = ConvertParseTreeError::FullyQualifiedPathsNotSupportedHere { + span: tilde_token.span(), + }; + return Err(ec.error(error)); + } + let generic_args = generics_opt.map(|(_, y)| y); + let type_args = match generic_args { + Some(x) => generic_args_to_type_arguments(ec, x)?, + None => Default::default(), + }; + Ok((name, type_args)) +} fn path_expr_segment_to_ident( ec: &mut ErrorContext, path_expr_segment: PathExprSegment, @@ -1911,6 +1944,54 @@ fn literal_to_literal( Ok(literal) } +/// Like [path_expr_to_call_path], but instead can potentially return type arguments. +/// Use this when converting a call path that could potentially include type arguments, i.e. the +/// turbofish. +fn path_expr_to_call_path_type_args( + ec: &mut ErrorContext, + path_expr: PathExpr, +) -> Result<(CallPath, Vec), ErrorEmitted> { + let PathExpr { + root_opt, + prefix, + mut suffix, + } = path_expr; + let is_absolute = path_root_opt_to_bool(ec, root_opt)?; + let (call_path, type_arguments) = match suffix.pop() { + Some((_double_colon_token, call_path_suffix)) => { + let mut prefixes = vec![path_expr_segment_to_ident(ec, prefix)?]; + for (_double_colon_token, call_path_prefix) in suffix { + let ident = path_expr_segment_to_ident(ec, call_path_prefix)?; + // note that call paths only support one set of type arguments per call path right + // now + prefixes.push(ident); + } + let (suffix, ty_args) = + path_expr_segment_to_ident_or_type_argument(ec, call_path_suffix)?; + ( + CallPath { + prefixes, + suffix, + is_absolute, + }, + ty_args, + ) + } + None => { + let (suffix, ty_args) = path_expr_segment_to_ident_or_type_argument(ec, prefix)?; + ( + CallPath { + prefixes: Default::default(), + suffix, + is_absolute, + }, + ty_args, + ) + } + }; + Ok((call_path, type_arguments)) +} + fn path_expr_to_call_path( ec: &mut ErrorContext, path_expr: PathExpr, @@ -2182,16 +2263,18 @@ fn dependency_to_include_statement(dependency: Dependency) -> IncludeStatement { } } -/* -fn generic_args_to_type_parameters(generic_args: GenericArgs) -> Vec { +#[allow(dead_code)] +fn generic_args_to_type_parameters( + ec: &mut ErrorContext, + generic_args: GenericArgs, +) -> Result, ErrorEmitted> { generic_args - .parameters - .into_inner() - .into_iter() - .map(ty_to_type_parameter) - .collect() + .parameters + .into_inner() + .into_iter() + .map(|x| ty_to_type_parameter(ec, x)) + .collect() } -*/ fn asm_register_declaration_to_asm_register_declaration( ec: &mut ErrorContext, @@ -2301,32 +2384,43 @@ fn pattern_to_scrutinee( Ok(scrutinee) } -/* -fn ty_to_type_parameter(ty: Ty) -> TypeParameter { +#[allow(dead_code)] +fn ty_to_type_parameter(ec: &mut ErrorContext, ty: Ty) -> Result { let name_ident = match ty { - Ty::Path(path_type) => path_type_to_ident(path_type), + Ty::Path(path_type) => path_type_to_ident(ec, path_type)?, + Ty::Infer { underscore_token } => { + return Ok(TypeParameter { + type_id: insert_type(TypeInfo::Unknown), + name_ident: underscore_token.into(), + trait_constraints: Default::default(), + }) + } Ty::Tuple(..) => panic!("tuple types are not allowed in this position"), Ty::Array(..) => panic!("array types are not allowed in this position"), Ty::Str { .. } => panic!("str types are not allowed in this position"), }; - TypeParameter { + Ok(TypeParameter { type_id: insert_type(TypeInfo::Custom { name: name_ident.clone(), type_arguments: Vec::new(), }), name_ident, trait_constraints: Vec::new(), - } + }) } -fn path_type_to_ident(path_type: PathType) -> Ident { - let PathType { root_opt, prefix, suffix } = path_type; +#[allow(dead_code)] +fn path_type_to_ident(ec: &mut ErrorContext, path_type: PathType) -> Result { + let PathType { + root_opt, + prefix, + suffix, + } = path_type; if root_opt.is_some() || !suffix.is_empty() { panic!("types with paths aren't currently supported"); } - path_type_segment_to_ident(prefix) + path_type_segment_to_ident(ec, prefix) } -*/ fn path_expr_to_ident(ec: &mut ErrorContext, path_expr: PathExpr) -> Result { let span = path_expr.span(); diff --git a/sway-core/src/optimize.rs b/sway-core/src/optimize.rs index 8d88f171a18..745060eb633 100644 --- a/sway-core/src/optimize.rs +++ b/sway-core/src/optimize.rs @@ -550,10 +550,9 @@ impl FnCompiler { TypedAstNodeContent::WhileLoop(twl) => { self.compile_while_loop(context, twl, span_md_idx) } - TypedAstNodeContent::SideEffect => Err(CompileError::Internal( - "unexpected side effect", - ast_node.span, - )), + // a side effect can be () because it just impacts the type system/namespacing. + // There should be no new IR generated. + TypedAstNodeContent::SideEffect => Ok(Constant::get_unit(context, None)), } }) .collect::, CompileError>>() diff --git a/sway-parse/src/keywords.rs b/sway-parse/src/keywords.rs index 7b3ef3a6418..8c14555e797 100644 --- a/sway-parse/src/keywords.rs +++ b/sway-parse/src/keywords.rs @@ -80,6 +80,16 @@ macro_rules! define_token ( pub fn span(&self) -> Span { self.span.clone() } + + pub fn ident(&self) -> Ident { + Ident::new(self.span()) + } + } + + impl From<$ty_name> for Ident { + fn from(o: $ty_name) -> Ident { + o.ident() + } } impl Peek for $ty_name { diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 046cac42ca2..2e79f84580a 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -297,6 +297,10 @@ pub fn run(filter_regex: Option) { "should_pass/language/import_trailing_comma", ProgramState::Return(0), ), + ( + "should_pass/language/primitive_type_argument", + ProgramState::Return(5), + ), ]; let mut number_of_tests_run = @@ -411,6 +415,7 @@ pub fn run(filter_regex: Option) { "should_fail/generics_unhelpful_error", "should_fail/generic_shadows_generic", "should_fail/different_contract_caller_types", + "should_fail/primitive_type_argument", ]; number_of_tests_run += negative_project_names.iter().fold(0, |acc, name| { if filter(name) { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/.gitignore b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/Forc.lock new file mode 100644 index 00000000000..6689d1448a9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/Forc.lock @@ -0,0 +1,11 @@ +[[package]] +name = 'core' +dependencies = [] + +[[package]] +name = 'std' +dependencies = ['core'] + +[[package]] +name = 'unused_generic' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/Forc.toml new file mode 100644 index 00000000000..3ab3188057c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "unused_generic" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/bar/baz.sw b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/bar/baz.sw new file mode 100644 index 00000000000..98b058842cb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/bar/baz.sw @@ -0,0 +1,5 @@ +library baz; + +pub struct ExampleStruct { + a_field: T +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/foo.sw b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/foo.sw new file mode 100644 index 00000000000..54aafe13c9d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/foo.sw @@ -0,0 +1,3 @@ +library foo; + +dep bar/baz; diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/main.sw new file mode 100644 index 00000000000..3e604a0cd3b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/src/main.sw @@ -0,0 +1,17 @@ +script; + +dep foo; + +struct S { } + +impl S { + fn f(self) -> u64 { + 5 + } +} + +fn main() -> u64 { + let a = S:: { }; + let b = foo::baz::ExampleStruct:: { a_field: 5u64 }; + return a.f(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/.gitignore b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/Forc.lock new file mode 100644 index 00000000000..6689d1448a9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/Forc.lock @@ -0,0 +1,11 @@ +[[package]] +name = 'core' +dependencies = [] + +[[package]] +name = 'std' +dependencies = ['core'] + +[[package]] +name = 'unused_generic' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/Forc.toml new file mode 100644 index 00000000000..3ab3188057c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "unused_generic" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/bar/baz.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/bar/baz.sw new file mode 100644 index 00000000000..7e0e8964ef4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/bar/baz.sw @@ -0,0 +1,8 @@ +library baz; + +dep quux; + +pub struct ExampleStruct { + a_field: T, + b_field: U +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/bar/quux.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/bar/quux.sw new file mode 100644 index 00000000000..88853a356ec --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/bar/quux.sw @@ -0,0 +1,9 @@ +library quux; +pub struct Quux { + a: A, + b: B, + c: C, + d: D, + e: E, + f: F +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/foo.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/foo.sw new file mode 100644 index 00000000000..54aafe13c9d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/foo.sw @@ -0,0 +1,3 @@ +library foo; + +dep bar/baz; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/main.sw new file mode 100644 index 00000000000..1ff14b77fda --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/src/main.sw @@ -0,0 +1,19 @@ +script; + +dep foo; + +struct S { } + +impl S { + fn f(self) -> u64 { + 5 + } +} + +fn main() -> u64 { + let a = S:: { }; + let b = foo::baz::ExampleStruct:: { a_field: 5u64, b_field: true }; + use foo::baz::ExampleStruct; + let c = foo::baz::quux::Quux::, u64, str[3], u64> { a: 10, b: true, c: b, d: 10, e: "foo", f: 10 }; + return a.f(); +}