From 25fd5e021b0fe16b74886a7450b4bbe1b95401a4 Mon Sep 17 00:00:00 2001 From: Yutaka HARA Date: Sun, 30 Jul 2023 23:32:21 +0900 Subject: [PATCH 1/2] Remove unreachable `then` --- lib/skc_ast2hir/src/type_system/type_checking.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/skc_ast2hir/src/type_system/type_checking.rs b/lib/skc_ast2hir/src/type_system/type_checking.rs index a25e810e..0f27f618 100644 --- a/lib/skc_ast2hir/src/type_system/type_checking.rs +++ b/lib/skc_ast2hir/src/type_system/type_checking.rs @@ -181,17 +181,10 @@ fn check_arg_type( return Ok(()); } - let msg = if inferred.is_some() { - format!( - "the argument `{}' of `{}' is inferred to {} but got {}", - param.name, sig.fullname, expected, arg_ty.fullname - ) - } else { - format!( - "the argument `{}' of `{}' should be {} but got {}", - param.name, sig.fullname, param.ty, arg_ty - ) - }; + let msg = format!( + "the argument `{}' of `{}' should be {} but got {}", + param.name, sig.fullname, param.ty, arg_ty + ); let locs = &arg_hir.locs; let report = skc_error::build_report(msg, locs, |r, locs_span| { r.with_label(Label::new(locs_span).with_message(&arg_hir.ty)) From 5cbd89c6be54ffa6760e553da85dbb86db0d8385 Mon Sep 17 00:00:00 2001 From: Yutaka HARA Date: Thu, 3 Aug 2023 22:10:06 +0900 Subject: [PATCH 2/2] fix: substitute param_tys after method-tyarg inference Once method-wise type arguments are inferred, you need to apply it to the signature before checking the argument types. --- builtin/enumerable.sk | 2 +- .../src/convert_exprs/method_call.rs | 50 ++++++++++++++----- .../src/type_system/type_checking.rs | 23 ++++----- lib/skc_hir/src/signature.rs | 4 ++ 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/builtin/enumerable.sk b/builtin/enumerable.sk index a4df45c7..fd5c30aa 100644 --- a/builtin/enumerable.sk +++ b/builtin/enumerable.sk @@ -37,7 +37,7 @@ module Enumerable # Like `map` but `f` should return an array and the result is flattened. def flat_map(f: Fn1>) -> Array - self.map>(f).fold(Array.new){|sum: Array, item: Array| + self.map(f).fold(Array.new){|sum: Array, item: Array| sum.append(item) sum } diff --git a/lib/skc_ast2hir/src/convert_exprs/method_call.rs b/lib/skc_ast2hir/src/convert_exprs/method_call.rs index 1d44ab18..4d9f9140 100644 --- a/lib/skc_ast2hir/src/convert_exprs/method_call.rs +++ b/lib/skc_ast2hir/src/convert_exprs/method_call.rs @@ -242,22 +242,13 @@ pub fn build( method_tyargs: Vec, locs: &LocationSpan, ) -> Result { - check_argument_types(mk, &found.sig, &receiver_hir, &mut arg_hirs, &inf)?; let receiver_ty = receiver_hir.ty.clone(); let specialized = receiver_hir.ty.is_specialized(); let first_arg_ty = arg_hirs.get(0).map(|x| x.ty.clone()); let arg_types = arg_hirs.iter().map(|x| x.ty.clone()).collect::>(); + let infer_tyargs = found.sig.has_typarams() && method_tyargs.is_empty(); - let receiver = Hir::bit_cast(found.owner.to_term_ty(), receiver_hir); - let args = if specialized { - arg_hirs - .into_iter() - .map(|expr| Hir::bit_cast(ty::raw("Object"), expr)) - .collect::>() - } else { - arg_hirs - }; - let tyargs = if method_tyargs.is_empty() { + let tyargs = if infer_tyargs { let err = error::method_tyarg_inference_failed( format!("Could not infer type arg(s) of {}", found.sig), locs, @@ -267,12 +258,45 @@ pub fn build( method_tyargs }; + let updated_param_types = if infer_tyargs { + found + .sig + .params + .iter() + .map(|param| param.ty.substitute(Default::default(), &tyargs)) + .collect::>() + } else { + found + .sig + .params + .iter() + .map(|param| param.ty.clone()) + .collect::>() + }; + check_argument_types( + mk, + &found.sig, + &receiver_hir, + &mut arg_hirs, + &updated_param_types, + )?; + + let args = if specialized { + arg_hirs + .into_iter() + .map(|expr| Hir::bit_cast(ty::raw("Object"), expr)) + .collect::>() + } else { + arg_hirs + }; + // Special handling for `Foo.new(x)` where `Foo` is a generic class and // `T` is inferred from `x`. if found.is_generic_new(&receiver_ty) { return Ok(call_specialized_new(mk, &receiver_ty, args, tyargs, locs)); } + let receiver = Hir::bit_cast(found.owner.to_term_ty(), receiver_hir); let hir = build_hir(mk, &found, receiver, args, tyargs, &inf); if found.sig.fullname.full_name == "Object#unsafe_cast" { Ok(Hir::bit_cast(first_arg_ty.unwrap().instance_ty(), hir)) @@ -286,9 +310,9 @@ fn check_argument_types( sig: &MethodSignature, receiver_hir: &HirExpression, arg_hirs: &mut [HirExpression], - inf: &Option, + arg_types: &[TermTy], ) -> Result<()> { - type_checking::check_method_args(&mk.class_dict, sig, receiver_hir, arg_hirs, inf)?; + type_checking::check_method_args(&mk.class_dict, sig, receiver_hir, arg_hirs, arg_types)?; if let Some(last_arg) = arg_hirs.last_mut() { check_break_in_block(sig, last_arg)?; } diff --git a/lib/skc_ast2hir/src/type_system/type_checking.rs b/lib/skc_ast2hir/src/type_system/type_checking.rs index 0f27f618..f3ae75c5 100644 --- a/lib/skc_ast2hir/src/type_system/type_checking.rs +++ b/lib/skc_ast2hir/src/type_system/type_checking.rs @@ -117,11 +117,11 @@ pub fn check_method_args( sig: &MethodSignature, _receiver_hir: &HirExpression, arg_hirs: &[HirExpression], - inf: &Option, + param_types: &[TermTy], ) -> Result<()> { let mut result = check_method_arity(sig, arg_hirs); if result.is_ok() { - result = check_arg_types(class_dict, sig, arg_hirs, inf); + result = check_arg_types(class_dict, sig, arg_hirs, param_types); } if result.is_err() { @@ -152,13 +152,13 @@ fn check_arg_types( class_dict: &ClassDict, sig: &MethodSignature, arg_hirs: &[HirExpression], - inf: &Option, + param_types: &[TermTy], ) -> Result<()> { for i in 0..sig.params.len() { let param = &sig.params[i]; let arg_hir = &arg_hirs[i]; - let inferred = inf.as_ref().map(|x| &x.solved_method_arg_tys[i]); - check_arg_type(class_dict, sig, arg_hir, param, &inferred)?; + let param_ty = ¶m_types[i]; + check_arg_type(class_dict, sig, arg_hir, param, param_ty)?; } Ok(()) } @@ -169,25 +169,20 @@ fn check_arg_type( sig: &MethodSignature, arg_hir: &HirExpression, param: &MethodParam, - inferred: &Option<&TermTy>, + param_ty: &TermTy, ) -> Result<()> { - if inferred.is_some() { - // Type inferrence succeed == no type error found - return Ok(()); - } - let expected = ¶m.ty; let arg_ty = &arg_hir.ty; - if class_dict.conforms(arg_ty, expected) { + if class_dict.conforms(arg_ty, param_ty) { return Ok(()); } let msg = format!( "the argument `{}' of `{}' should be {} but got {}", - param.name, sig.fullname, param.ty, arg_ty + param.name, sig.fullname, param_ty, arg_ty ); let locs = &arg_hir.locs; let report = skc_error::build_report(msg, locs, |r, locs_span| { - r.with_label(Label::new(locs_span).with_message(&arg_hir.ty)) + r.with_label(Label::new(locs_span).with_message(&arg_ty)) }); Err(type_error(report)) } diff --git a/lib/skc_hir/src/signature.rs b/lib/skc_hir/src/signature.rs index 8d218635..7b8b3369 100644 --- a/lib/skc_hir/src/signature.rs +++ b/lib/skc_hir/src/signature.rs @@ -24,6 +24,10 @@ impl MethodSignature { self.params.iter().any(|p| p.has_default) } + pub fn has_typarams(&self) -> bool { + !self.typarams.is_empty() + } + pub fn is_class_method(&self) -> bool { self.fullname.type_name.is_meta() }