From 91e1ae07ce1d0c667bed1481687fc2084635df8c Mon Sep 17 00:00:00 2001 From: xunilrj Date: Fri, 23 Aug 2024 15:40:21 +0100 Subject: [PATCH 1/8] check if all required generic arguments are valid --- .../src/language/ty/expression/expression.rs | 18 +++++++++++ .../typed_expression/function_application.rs | 30 +++++++++++++++++-- .../missing_type_parameters/src/main.sw | 7 +++++ .../missing_type_parameters/test.toml | 3 ++ .../test_contracts/basic_storage/src/main.sw | 2 +- .../storage_namespace/src/main.sw | 2 +- 6 files changed, 57 insertions(+), 5 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index b442e0ba0cd..0d8377c4d64 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -111,6 +111,24 @@ impl TypeCheckAnalysis for TyExpression { ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { match &self.expression { + TyExpressionVariant::FunctionApplication { type_binding, .. } => { + if let Some(type_binding) = type_binding { + let args = match &type_binding.type_arguments { + TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, + }; + for arg in args { + match &*ctx.engines.te().get(arg.type_id) { + TypeInfo::Placeholder(_) => { + let _ = handler.emit_err(CompileError::UnableToInferGeneric { + ty: ctx.engines.help_out(arg.type_id).to_string(), + span: arg.span.clone(), + }); + } + _ => {} + } + } + } + } // Check literal "fits" into assigned typed. TyExpressionVariant::Literal(Literal::Numeric(literal_value)) => { let t = ctx.engines.te().get(self.return_type); diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs index cf39c305572..9904dc8077c 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs @@ -93,7 +93,7 @@ pub(crate) fn instantiate_function_application( let function_is_trait_method_dummy = function_decl.is_trait_method_dummy; let new_decl_ref = decl_engine .insert( - function_decl, + function_decl.clone(), decl_engine .get_parsed_decl_id(function_decl_ref.id()) .as_ref(), @@ -115,13 +115,37 @@ pub(crate) fn instantiate_function_application( new_decl_ref }; + let call_path = call_path_binding.inner.clone(); + let mut type_binding = call_path_binding.strip_inner(); + + // Normalize all function calls to have the same generic argument count + // as the definition + if function_decl.type_parameters.len() != type_binding.type_arguments.as_slice().len() { + type_binding.type_arguments = TypeArgs::Regular( + function_decl + .type_parameters + .iter() + .map(|x| TypeArgument { + type_id: x.type_id, + initial_type_id: x.initial_type_id, + span: span.clone(), + call_path_tree: None, + }) + .collect(), + ); + } + assert_eq!( + function_decl.type_parameters.len(), + type_binding.type_arguments.as_slice().len() + ); + let exp = ty::TyExpression { expression: ty::TyExpressionVariant::FunctionApplication { - call_path: call_path_binding.inner.clone(), + call_path, arguments: typed_arguments_with_names, fn_ref: new_decl_ref, selector: None, - type_binding: Some(call_path_binding.strip_inner()), + type_binding: Some(type_binding), call_path_typeid: None, contract_call_params: IndexMap::new(), contract_caller: None, diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw index 6927766f189..0392696b4f1 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw @@ -2,9 +2,16 @@ script; fn main() { let g: bool = three_generics(true, "foo", 10); + + // Should fail because compiler cannot infer generic argument + one_generic(); } fn three_generics(a: A, b: B, c: C) -> A { let new_a: A = a; new_a } + +fn one_generic() { + +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml index af45dbb8e7e..ac1d2d85e8e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml @@ -6,3 +6,6 @@ category = "fail" # check: $()Unknown type name "B" # check: $()Could not find symbol "C" in this scope. # check: $()Unknown type name "C" + +# check: $()one_generic() +# nextln: $()Cannot infer type for type parameter "T". Insufficient type information provided. Try annotating its type. diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw index 261bd8cb0e8..532709bfed7 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw @@ -303,6 +303,6 @@ fn test_storage() { // If these comparisons are done inline just above then it blows out the register allocator due to // all the ASM blocks. #[inline(never)] -fn assert_streq(lhs: S1, rhs: str) { +fn assert_streq(lhs: S1, rhs: str) { assert(sha256_str_array(lhs) == sha256(rhs)); } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw index 934376a211b..d84c9d9ee3b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw @@ -291,6 +291,6 @@ fn test_storage() { // If these comparisons are done inline just above then it blows out the register allocator due to // all the ASM blocks. #[inline(never)] -fn assert_streq(lhs: S1, rhs: str) { +fn assert_streq(lhs: S1, rhs: str) { assert(sha256_str_array(lhs) == sha256(rhs)); } From 81c63a08e811549cca4c2d3059d61735e8e77024 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Fri, 23 Aug 2024 15:52:50 +0100 Subject: [PATCH 2/8] fmt and clippy issues --- .../src/language/ty/expression/expression.rs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 0d8377c4d64..f0b1658b76a 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -111,21 +111,19 @@ impl TypeCheckAnalysis for TyExpression { ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { match &self.expression { - TyExpressionVariant::FunctionApplication { type_binding, .. } => { - if let Some(type_binding) = type_binding { - let args = match &type_binding.type_arguments { - TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, - }; - for arg in args { - match &*ctx.engines.te().get(arg.type_id) { - TypeInfo::Placeholder(_) => { - let _ = handler.emit_err(CompileError::UnableToInferGeneric { - ty: ctx.engines.help_out(arg.type_id).to_string(), - span: arg.span.clone(), - }); - } - _ => {} - } + TyExpressionVariant::FunctionApplication { + type_binding: Some(type_binding), + .. + } => { + let args = match &type_binding.type_arguments { + TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, + }; + for arg in args { + if let TypeInfo::Placeholder(_) = &*ctx.engines.te().get(arg.type_id) { + let _ = handler.emit_err(CompileError::UnableToInferGeneric { + ty: ctx.engines.help_out(arg.type_id).to_string(), + span: arg.span.clone(), + }); } } } From cebef366769324f3806b27d1c46433570c7227d4 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Mon, 26 Aug 2024 13:18:42 +0100 Subject: [PATCH 3/8] moving type binding normalization to its type_check --- .../typed_expression/function_application.rs | 30 ++----------------- .../src/type_system/ast_elements/binding.rs | 27 ++++++++++++++++- .../missing_type_parameters/src/main.sw | 13 +++++--- .../missing_type_parameters/test.toml | 9 ++++++ 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs index 9904dc8077c..cf39c305572 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs @@ -93,7 +93,7 @@ pub(crate) fn instantiate_function_application( let function_is_trait_method_dummy = function_decl.is_trait_method_dummy; let new_decl_ref = decl_engine .insert( - function_decl.clone(), + function_decl, decl_engine .get_parsed_decl_id(function_decl_ref.id()) .as_ref(), @@ -115,37 +115,13 @@ pub(crate) fn instantiate_function_application( new_decl_ref }; - let call_path = call_path_binding.inner.clone(); - let mut type_binding = call_path_binding.strip_inner(); - - // Normalize all function calls to have the same generic argument count - // as the definition - if function_decl.type_parameters.len() != type_binding.type_arguments.as_slice().len() { - type_binding.type_arguments = TypeArgs::Regular( - function_decl - .type_parameters - .iter() - .map(|x| TypeArgument { - type_id: x.type_id, - initial_type_id: x.initial_type_id, - span: span.clone(), - call_path_tree: None, - }) - .collect(), - ); - } - assert_eq!( - function_decl.type_parameters.len(), - type_binding.type_arguments.as_slice().len() - ); - let exp = ty::TyExpression { expression: ty::TyExpressionVariant::FunctionApplication { - call_path, + call_path: call_path_binding.inner.clone(), arguments: typed_arguments_with_names, fn_ref: new_decl_ref, selector: None, - type_binding: Some(type_binding), + type_binding: Some(call_path_binding.strip_inner()), call_path_typeid: None, contract_call_params: IndexMap::new(), contract_caller: None, diff --git a/sway-core/src/type_system/ast_elements/binding.rs b/sway-core/src/type_system/ast_elements/binding.rs index d6022709fa6..3d7036b925c 100644 --- a/sway-core/src/type_system/ast_elements/binding.rs +++ b/sway-core/src/type_system/ast_elements/binding.rs @@ -4,7 +4,8 @@ use sway_types::{Span, Spanned}; use crate::{ decl_engine::{ - parsed_id::ParsedDeclId, DeclEngineGetParsedDeclId, DeclEngineInsert, DeclId, DeclRef, + parsed_id::ParsedDeclId, DeclEngineGet, DeclEngineGetParsedDeclId, DeclEngineInsert, + DeclId, DeclRef, }, engine_threading::{EqWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext}, language::{ @@ -350,6 +351,30 @@ impl TypeCheckTypeBinding for TypeBinding { decl_engine.get_parsed_decl_id(fn_ref.id()).as_ref(), ) .with_parent(ctx.engines.de(), fn_ref.id().into()); + + let decl = ctx.engines.de().get(new_fn_ref.id()); + + // Normalize all function calls to have the same generic argument count + // as the definition + if decl.type_parameters.len() != self.type_arguments.as_slice().len() { + self.type_arguments = TypeArgs::Regular( + decl.type_parameters + .iter() + .map(|x| TypeArgument { + type_id: x.type_id, + initial_type_id: x.initial_type_id, + span: self.span.clone(), + call_path_tree: None, + }) + .collect(), + ); + } + + assert_eq!( + decl.type_parameters.len(), + self.type_arguments.as_slice().len() + ); + Ok((new_fn_ref, None, None)) } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw index 0392696b4f1..33e6982aacc 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw @@ -3,8 +3,14 @@ script; fn main() { let g: bool = three_generics(true, "foo", 10); - // Should fail because compiler cannot infer generic argument + // Should fail because compiler cannot infer generic argument T one_generic(); + + // Should fail because compiler cannot infer generic arguments A, B + two_generics(); + + // Two generics arguments expected + two_generics::(); } fn three_generics(a: A, b: B, c: C) -> A { @@ -12,6 +18,5 @@ fn three_generics(a: A, b: B, c: C) -> A { new_a } -fn one_generic() { - -} +fn one_generic() { } +fn two_generics() { } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml index ac1d2d85e8e..b778238892f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml @@ -7,5 +7,14 @@ category = "fail" # check: $()Could not find symbol "C" in this scope. # check: $()Unknown type name "C" +# check: $()two_generics::(); +# nextln: $()Expected 2 type arguments for "two_generics", but instead found 1. + # check: $()one_generic() # nextln: $()Cannot infer type for type parameter "T". Insufficient type information provided. Try annotating its type. + +# check: $()two_generics(); +# nextln: $()Cannot infer type for type parameter "A". Insufficient type information provided. Try annotating its type. + +# check: $()two_generics(); +# nextln: $()Cannot infer type for type parameter "B". Insufficient type information provided. Try annotating its type. From 541615493981b2ac504cfc1e3d3ee6c10e9a06f7 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Mon, 26 Aug 2024 16:43:26 +0100 Subject: [PATCH 4/8] better type arguments normalization --- .../src/type_system/ast_elements/binding.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sway-core/src/type_system/ast_elements/binding.rs b/sway-core/src/type_system/ast_elements/binding.rs index 3d7036b925c..31ac8298604 100644 --- a/sway-core/src/type_system/ast_elements/binding.rs +++ b/sway-core/src/type_system/ast_elements/binding.rs @@ -354,26 +354,27 @@ impl TypeCheckTypeBinding for TypeBinding { let decl = ctx.engines.de().get(new_fn_ref.id()); - // Normalize all function calls to have the same generic argument count - // as the definition + // Complete missing type parameters using TypeId from the declaration if decl.type_parameters.len() != self.type_arguments.as_slice().len() { - self.type_arguments = TypeArgs::Regular( + let args = match &mut self.type_arguments { + TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, + }; + + let args_len = args.len(); + args.extend( decl.type_parameters .iter() + .skip(args_len) .map(|x| TypeArgument { type_id: x.type_id, initial_type_id: x.initial_type_id, span: self.span.clone(), call_path_tree: None, - }) - .collect(), + }), ); } - assert_eq!( - decl.type_parameters.len(), - self.type_arguments.as_slice().len() - ); + assert!(self.type_arguments.as_slice().len() >= decl.type_parameters.len(),); Ok((new_fn_ref, None, None)) } From a22f9de8d9bd9803431b021c36e347f635cc9ad8 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 27 Aug 2024 13:58:59 +0100 Subject: [PATCH 5/8] check unresolved generic arguments in method applications --- .../src/language/ty/expression/expression.rs | 1 + .../ty/expression/expression_variant.rs | 34 +++++++++++++++++++ .../typed_expression/function_application.rs | 29 ++++++++-------- .../typed_expression/method_application.rs | 3 +- .../src/type_system/ast_elements/binding.rs | 28 +-------------- .../missing_type_parameters/src/main.sw | 18 +++++++++- .../missing_type_parameters/test.toml | 12 +++++++ 7 files changed, 82 insertions(+), 43 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index f0b1658b76a..4c4059a1125 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -111,6 +111,7 @@ impl TypeCheckAnalysis for TyExpression { ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { match &self.expression { + // Collect unresolved generic arguments TyExpressionVariant::FunctionApplication { type_binding: Some(type_binding), .. diff --git a/sway-core/src/language/ty/expression/expression_variant.rs b/sway-core/src/language/ty/expression/expression_variant.rs index 4f5643a5020..4a394a41f4c 100644 --- a/sway-core/src/language/ty/expression/expression_variant.rs +++ b/sway-core/src/language/ty/expression/expression_variant.rs @@ -179,6 +179,40 @@ impl TyExpressionVariant { _ => None, } } + + /// When the expression has type arguments, it normalizes the type binding + /// to have the same amount and use TypeId from the declaration. + pub fn normalize_type_args(&mut self, engines: &Engines, span: &Span) { + if let TyExpressionVariant::FunctionApplication { + fn_ref, + type_binding: Some(type_binding), + .. + } = self + { + let decl = engines.de().get(fn_ref.id()); + + if decl.type_parameters.len() != type_binding.type_arguments.as_slice().len() { + let args = match &mut type_binding.type_arguments { + TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, + }; + + let args_len = args.len(); + args.extend( + decl.type_parameters + .iter() + .skip(args_len) + .map(|x| TypeArgument { + type_id: x.type_id, + initial_type_id: x.initial_type_id, + span: span.clone(), + call_path_tree: None, + }), + ); + } + + assert!(type_binding.type_arguments.as_slice().len() >= decl.type_parameters.len(),); + } + } } impl EqWithEngines for TyExpressionVariant {} diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs index cf39c305572..cca692b4e9a 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs @@ -115,22 +115,23 @@ pub(crate) fn instantiate_function_application( new_decl_ref }; - let exp = ty::TyExpression { - expression: ty::TyExpressionVariant::FunctionApplication { - call_path: call_path_binding.inner.clone(), - arguments: typed_arguments_with_names, - fn_ref: new_decl_ref, - selector: None, - type_binding: Some(call_path_binding.strip_inner()), - call_path_typeid: None, - contract_call_params: IndexMap::new(), - contract_caller: None, - }, - return_type: function_return_type_id, - span, + let mut expression = ty::TyExpressionVariant::FunctionApplication { + call_path: call_path_binding.inner.clone(), + arguments: typed_arguments_with_names, + fn_ref: new_decl_ref, + selector: None, + type_binding: Some(call_path_binding.strip_inner()), + call_path_typeid: None, + contract_call_params: IndexMap::new(), + contract_caller: None, }; + expression.normalize_type_args(engines, &span); - Ok(exp) + Ok(ty::TyExpression { + expression, + return_type: function_return_type_id, + span, + }) } /// Type checks the arguments. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index ea83cd08847..4a076f8970f 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -707,7 +707,7 @@ pub(crate) fn type_check_method_application( } } - let fn_app = ty::TyExpressionVariant::FunctionApplication { + let mut fn_app = ty::TyExpressionVariant::FunctionApplication { call_path: call_path.clone(), arguments, fn_ref, @@ -717,6 +717,7 @@ pub(crate) fn type_check_method_application( contract_call_params: contract_call_params_map, contract_caller: None, }; + fn_app.normalize_type_args(engines, &span); let exp = ty::TyExpression { expression: fn_app.clone(), diff --git a/sway-core/src/type_system/ast_elements/binding.rs b/sway-core/src/type_system/ast_elements/binding.rs index 31ac8298604..d6022709fa6 100644 --- a/sway-core/src/type_system/ast_elements/binding.rs +++ b/sway-core/src/type_system/ast_elements/binding.rs @@ -4,8 +4,7 @@ use sway_types::{Span, Spanned}; use crate::{ decl_engine::{ - parsed_id::ParsedDeclId, DeclEngineGet, DeclEngineGetParsedDeclId, DeclEngineInsert, - DeclId, DeclRef, + parsed_id::ParsedDeclId, DeclEngineGetParsedDeclId, DeclEngineInsert, DeclId, DeclRef, }, engine_threading::{EqWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext}, language::{ @@ -351,31 +350,6 @@ impl TypeCheckTypeBinding for TypeBinding { decl_engine.get_parsed_decl_id(fn_ref.id()).as_ref(), ) .with_parent(ctx.engines.de(), fn_ref.id().into()); - - let decl = ctx.engines.de().get(new_fn_ref.id()); - - // Complete missing type parameters using TypeId from the declaration - if decl.type_parameters.len() != self.type_arguments.as_slice().len() { - let args = match &mut self.type_arguments { - TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, - }; - - let args_len = args.len(); - args.extend( - decl.type_parameters - .iter() - .skip(args_len) - .map(|x| TypeArgument { - type_id: x.type_id, - initial_type_id: x.initial_type_id, - span: self.span.clone(), - call_path_tree: None, - }), - ); - } - - assert!(self.type_arguments.as_slice().len() >= decl.type_parameters.len(),); - Ok((new_fn_ref, None, None)) } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw index 33e6982aacc..65092e9f575 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw @@ -1,8 +1,15 @@ script; +struct S {} + +impl S { + fn one_generic(self) { } + fn two_generics(self) { } +} + fn main() { let g: bool = three_generics(true, "foo", 10); - + // Should fail because compiler cannot infer generic argument T one_generic(); @@ -11,6 +18,15 @@ fn main() { // Two generics arguments expected two_generics::(); + + // Should fail because compiler cannot infer generic argument T + S{}.one_generic(); + + // Should fail because compiler cannot infer generic arguments A, B + S{}.two_generics(); + + // Two generics arguments expected + S{}.two_generics::(); } fn three_generics(a: A, b: B, c: C) -> A { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml index b778238892f..80456818207 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml @@ -10,6 +10,9 @@ category = "fail" # check: $()two_generics::(); # nextln: $()Expected 2 type arguments for "two_generics", but instead found 1. +# check: $()S{}.two_generics::(); +# nextln: $()Expected 2 type arguments for "two_generics", but instead found 1. + # check: $()one_generic() # nextln: $()Cannot infer type for type parameter "T". Insufficient type information provided. Try annotating its type. @@ -18,3 +21,12 @@ category = "fail" # check: $()two_generics(); # nextln: $()Cannot infer type for type parameter "B". Insufficient type information provided. Try annotating its type. + +# check: $()S{}.one_generic() +# nextln: $()Cannot infer type for type parameter "T". Insufficient type information provided. Try annotating its type. + +# check: $()S{}.two_generics(); +# nextln: $()Cannot infer type for type parameter "A". Insufficient type information provided. Try annotating its type. + +# check: $()S{}.two_generics(); +# nextln: $()Cannot infer type for type parameter "B". Insufficient type information provided. Try annotating its type. From f5fe228f50a2de3eba8e0921213e356c4754f369 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 27 Aug 2024 15:40:16 +0100 Subject: [PATCH 6/8] better fix for unresolved generics --- .../src/language/ty/expression/expression.rs | 49 ++++++++++++++----- .../ty/expression/expression_variant.rs | 34 ------------- .../typed_expression/function_application.rs | 29 ++++++----- .../typed_expression/method_application.rs | 3 +- 4 files changed, 51 insertions(+), 64 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 4c4059a1125..73d3b2ec6f1 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -111,22 +111,40 @@ impl TypeCheckAnalysis for TyExpression { ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { match &self.expression { - // Collect unresolved generic arguments + // Search for TypeInfo::Placeholder TyExpressionVariant::FunctionApplication { + fn_ref, type_binding: Some(type_binding), .. } => { - let args = match &type_binding.type_arguments { - TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, - }; - for arg in args { - if let TypeInfo::Placeholder(_) = &*ctx.engines.te().get(arg.type_id) { - let _ = handler.emit_err(CompileError::UnableToInferGeneric { - ty: ctx.engines.help_out(arg.type_id).to_string(), - span: arg.span.clone(), - }); - } - } + // let decl = ctx.engines.de().get(fn_ref.id()); + // for (idx, p) in decl.type_parameters.iter().enumerate() { + // if let TypeInfo::Placeholder(_) = &*ctx.engines.te().get(p.type_id) { + // let span = type_binding + // .type_arguments + // .as_slice() + // .get(idx) + // .map(|x| x.span.clone()) + // .unwrap_or_else(|| type_binding.span.clone()); + + // let _ = handler.emit_err(CompileError::UnableToInferGeneric { + // ty: ctx.engines.help_out(p.type_id).to_string(), + // span, + // }); + // } + // } + + // let args = match &type_binding.type_arguments { + // TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, + // }; + // for arg in args { + // if let TypeInfo::Placeholder(_) = &*ctx.engines.te().get(arg.type_id) { + // let _ = handler.emit_err(CompileError::UnableToInferGeneric { + // ty: ctx.engines.help_out(arg.type_id).to_string(), + // span: arg.span.clone(), + // }); + // } + // } } // Check literal "fits" into assigned typed. TyExpressionVariant::Literal(Literal::Numeric(literal_value)) => { @@ -208,7 +226,12 @@ impl CollectTypesMetadata for TyExpression { ctx.call_site_push(); for type_parameter in &function_decl.type_parameters { - ctx.call_site_insert(type_parameter.type_id, call_path.span()) + ctx.call_site_insert(type_parameter.type_id, call_path.span()); + res.append( + &mut type_parameter + .type_id + .collect_types_metadata(handler, ctx)? + ); } for content in function_decl.body.contents.iter() { diff --git a/sway-core/src/language/ty/expression/expression_variant.rs b/sway-core/src/language/ty/expression/expression_variant.rs index 4a394a41f4c..4f5643a5020 100644 --- a/sway-core/src/language/ty/expression/expression_variant.rs +++ b/sway-core/src/language/ty/expression/expression_variant.rs @@ -179,40 +179,6 @@ impl TyExpressionVariant { _ => None, } } - - /// When the expression has type arguments, it normalizes the type binding - /// to have the same amount and use TypeId from the declaration. - pub fn normalize_type_args(&mut self, engines: &Engines, span: &Span) { - if let TyExpressionVariant::FunctionApplication { - fn_ref, - type_binding: Some(type_binding), - .. - } = self - { - let decl = engines.de().get(fn_ref.id()); - - if decl.type_parameters.len() != type_binding.type_arguments.as_slice().len() { - let args = match &mut type_binding.type_arguments { - TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, - }; - - let args_len = args.len(); - args.extend( - decl.type_parameters - .iter() - .skip(args_len) - .map(|x| TypeArgument { - type_id: x.type_id, - initial_type_id: x.initial_type_id, - span: span.clone(), - call_path_tree: None, - }), - ); - } - - assert!(type_binding.type_arguments.as_slice().len() >= decl.type_parameters.len(),); - } - } } impl EqWithEngines for TyExpressionVariant {} diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs index cca692b4e9a..cf39c305572 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs @@ -115,23 +115,22 @@ pub(crate) fn instantiate_function_application( new_decl_ref }; - let mut expression = ty::TyExpressionVariant::FunctionApplication { - call_path: call_path_binding.inner.clone(), - arguments: typed_arguments_with_names, - fn_ref: new_decl_ref, - selector: None, - type_binding: Some(call_path_binding.strip_inner()), - call_path_typeid: None, - contract_call_params: IndexMap::new(), - contract_caller: None, - }; - expression.normalize_type_args(engines, &span); - - Ok(ty::TyExpression { - expression, + let exp = ty::TyExpression { + expression: ty::TyExpressionVariant::FunctionApplication { + call_path: call_path_binding.inner.clone(), + arguments: typed_arguments_with_names, + fn_ref: new_decl_ref, + selector: None, + type_binding: Some(call_path_binding.strip_inner()), + call_path_typeid: None, + contract_call_params: IndexMap::new(), + contract_caller: None, + }, return_type: function_return_type_id, span, - }) + }; + + Ok(exp) } /// Type checks the arguments. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 4a076f8970f..ea83cd08847 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -707,7 +707,7 @@ pub(crate) fn type_check_method_application( } } - let mut fn_app = ty::TyExpressionVariant::FunctionApplication { + let fn_app = ty::TyExpressionVariant::FunctionApplication { call_path: call_path.clone(), arguments, fn_ref, @@ -717,7 +717,6 @@ pub(crate) fn type_check_method_application( contract_call_params: contract_call_params_map, contract_caller: None, }; - fn_app.normalize_type_args(engines, &span); let exp = ty::TyExpression { expression: fn_app.clone(), From e4a214679b33403118e38a936126eb37825dc4ea Mon Sep 17 00:00:00 2001 From: xunilrj Date: Tue, 27 Aug 2024 15:44:11 +0100 Subject: [PATCH 7/8] removing old solution --- .../src/language/ty/expression/expression.rs | 37 +------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 73d3b2ec6f1..22b090c7423 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -111,41 +111,6 @@ impl TypeCheckAnalysis for TyExpression { ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { match &self.expression { - // Search for TypeInfo::Placeholder - TyExpressionVariant::FunctionApplication { - fn_ref, - type_binding: Some(type_binding), - .. - } => { - // let decl = ctx.engines.de().get(fn_ref.id()); - // for (idx, p) in decl.type_parameters.iter().enumerate() { - // if let TypeInfo::Placeholder(_) = &*ctx.engines.te().get(p.type_id) { - // let span = type_binding - // .type_arguments - // .as_slice() - // .get(idx) - // .map(|x| x.span.clone()) - // .unwrap_or_else(|| type_binding.span.clone()); - - // let _ = handler.emit_err(CompileError::UnableToInferGeneric { - // ty: ctx.engines.help_out(p.type_id).to_string(), - // span, - // }); - // } - // } - - // let args = match &type_binding.type_arguments { - // TypeArgs::Regular(args) | TypeArgs::Prefix(args) => args, - // }; - // for arg in args { - // if let TypeInfo::Placeholder(_) = &*ctx.engines.te().get(arg.type_id) { - // let _ = handler.emit_err(CompileError::UnableToInferGeneric { - // ty: ctx.engines.help_out(arg.type_id).to_string(), - // span: arg.span.clone(), - // }); - // } - // } - } // Check literal "fits" into assigned typed. TyExpressionVariant::Literal(Literal::Numeric(literal_value)) => { let t = ctx.engines.te().get(self.return_type); @@ -230,7 +195,7 @@ impl CollectTypesMetadata for TyExpression { res.append( &mut type_parameter .type_id - .collect_types_metadata(handler, ctx)? + .collect_types_metadata(handler, ctx)?, ); } From abed3d3b4adb57f1762ecf0c876d2353122de9b5 Mon Sep 17 00:00:00 2001 From: xunilrj Date: Wed, 28 Aug 2024 09:02:28 +0100 Subject: [PATCH 8/8] better error message --- .../src/language/ty/expression/expression.rs | 26 ++++++++++++++++--- sway-core/src/type_system/id.rs | 6 +++-- .../missing_type_parameters/src/main.sw | 5 ++++ .../missing_type_parameters/test.toml | 3 +++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index 22b090c7423..96598e0ab8f 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -182,6 +182,7 @@ impl CollectTypesMetadata for TyExpression { arguments, fn_ref, call_path, + type_binding, .. } => { for arg in arguments.iter() { @@ -190,12 +191,29 @@ impl CollectTypesMetadata for TyExpression { let function_decl = decl_engine.get_function(fn_ref); ctx.call_site_push(); - for type_parameter in &function_decl.type_parameters { + for (idx, type_parameter) in function_decl.type_parameters.iter().enumerate() { ctx.call_site_insert(type_parameter.type_id, call_path.span()); - res.append( - &mut type_parameter + + // Verify type arguments are concrete + res.extend( + type_parameter .type_id - .collect_types_metadata(handler, ctx)?, + .collect_types_metadata(handler, ctx)? + .into_iter() + // try to use the caller span for better error messages + .map(|x| match x { + TypeMetadata::UnresolvedType(ident, original_span) => { + let span = type_binding + .as_ref() + .and_then(|type_binding| { + type_binding.type_arguments.as_slice().get(idx) + }) + .map(|type_argument| Some(type_argument.span.clone())) + .unwrap_or(original_span); + TypeMetadata::UnresolvedType(ident, span) + } + x => x, + }), ); } diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 9410fd2d6ac..f928cbdaef5 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -61,8 +61,10 @@ impl CollectTypesMetadata for TypeId { ctx: &mut CollectTypesMetadataContext, ) -> Result, ErrorEmitted> { fn filter_fn(type_info: &TypeInfo) -> bool { - matches!(type_info, TypeInfo::UnknownGeneric { .. }) - || matches!(type_info, TypeInfo::Placeholder(_)) + matches!( + type_info, + TypeInfo::UnknownGeneric { .. } | TypeInfo::Placeholder(_) + ) } let engines = ctx.engines; let possible = self.extract_any_including_self(engines, &filter_fn, vec![], 0); diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw index 65092e9f575..08605fa28fa 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/src/main.sw @@ -7,6 +7,8 @@ impl S { fn two_generics(self) { } } +struct W { } + fn main() { let g: bool = three_generics(true, "foo", 10); @@ -27,6 +29,9 @@ fn main() { // Two generics arguments expected S{}.two_generics::(); + + // Missing generic argument of W + one_generic::(); } fn three_generics(a: A, b: B, c: C) -> A { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml index 80456818207..471410b9291 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_type_parameters/test.toml @@ -30,3 +30,6 @@ category = "fail" # check: $()S{}.two_generics(); # nextln: $()Cannot infer type for type parameter "B". Insufficient type information provided. Try annotating its type. + +# check: $()one_generic::(); +# nextln: $()Cannot infer type for type parameter "A". Insufficient type information provided. Try annotating its type.