From d750d8f6cf66a7eb8e7fc398f58ffb1bfafbb273 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Wed, 10 Apr 2024 10:29:15 -0700 Subject: [PATCH] [move 2024][alpha] Type inference holes (#17059) ## Description - Added placeholders for type inference `_` ## Test Plan - Added tests --- If your changes are not user-facing and do not break anything, you can skip the following section. Otherwise, please briefly describe what has changed under the Release Notes section. ### Type of Change (Check all that apply) - [ ] protocol change - [X] user-visible impact - [ ] breaking change for a client SDKs - [ ] breaking change for FNs (FN binary must upgrade) - [ ] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [ ] necessitate either a data wipe or data migration ### Release notes Move 2024.alpha now supports placeholders for type inference `_` --------- Co-authored-by: Todd Nowacki Co-authored-by: Cam Swords --- crates/move-compiler/src/diagnostics/codes.rs | 1 + crates/move-compiler/src/editions/mod.rs | 4 +- .../move-compiler/src/expansion/translate.rs | 28 ++- crates/move-compiler/src/naming/translate.rs | 166 +++++++++++++----- crates/move-compiler/src/typing/core.rs | 85 +++++++-- crates/move-compiler/src/typing/translate.rs | 25 +-- ...acro_identifier_invalid_type_parameter.exp | 24 +++ ...cro_identifier_invalid_type_parameter.move | 4 + .../macro_identifier_valid_names.move | 2 +- .../expansion/type_hole_as_type_param.exp | 26 +++ .../expansion/type_hole_as_type_param.move | 5 + .../naming/macro_identifier_assignment.exp | 24 +-- .../naming/type_hole_invalid_location.exp | 54 ++++++ .../naming/type_hole_invalid_location.move | 8 + .../type_hole_invalid_location_nested.exp | 68 +++++++ .../type_hole_invalid_location_nested.move | 11 ++ .../naming/type_hole_valid_location.move | 37 ++++ ...type_hole_valid_location_invalid_usage.exp | 14 ++ ...ype_hole_valid_location_invalid_usage.move | 9 + .../type_hole_valid_location_lambda.move | 11 ++ .../tests/move_2024/parser/type_hole.move | 10 ++ .../lambda_annotated_return_type_invalid.exp | 22 +-- .../lambda_return_invalid_conditional.exp | 20 +-- ...a_subtyping_usage_respects_annotations.exp | 5 +- .../typing/macros_lambdas_checked_invalid.exp | 39 ++-- .../macros_lambdas_checked_invalid_arity.exp | 60 +++---- .../typing/macros_return_checked_invalid.exp | 35 ++-- .../move_2024/typing/type_hole_macro.move | 34 ++++ .../typing/type_hole_macro_lambda.move | 25 +++ ...ype_hole_macro_lambda_with_annotaitons.exp | 46 +++++ ...pe_hole_macro_lambda_with_annotaitons.move | 26 +++ .../typing/type_hole_no_inferrence.exp | 18 ++ .../typing/type_hole_no_inferrence.move | 9 + .../type_hole_no_inferrence_threaded.exp | 9 + .../type_hole_no_inferrence_threaded.move | 13 ++ .../expansion/type_parameter_underscore.move | 4 + .../naming/type_hole_gated_invalid_usage.exp | 30 ++++ .../naming/type_hole_gated_invalid_usage.move | 4 + .../naming/type_hole_gated_valid_usage.exp | 8 + .../naming/type_hole_gated_valid_usage.move | 6 + 40 files changed, 850 insertions(+), 179 deletions(-) create mode 100644 crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.exp create mode 100644 crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.move create mode 100644 crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.exp create mode 100644 crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.move create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.exp create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.move create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.exp create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.move create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_valid_location.move create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.exp create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.move create mode 100644 crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_lambda.move create mode 100644 crates/move-compiler/tests/move_2024/parser/type_hole.move create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_macro.move create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda.move create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.exp create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.exp create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.move create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.exp create mode 100644 crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.move create mode 100644 crates/move-compiler/tests/move_check/expansion/type_parameter_underscore.move create mode 100644 crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.exp create mode 100644 crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.move create mode 100644 crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.exp create mode 100644 crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.move diff --git a/crates/move-compiler/src/diagnostics/codes.rs b/crates/move-compiler/src/diagnostics/codes.rs index d5a42f5b11..dc4c5dd6a5 100644 --- a/crates/move-compiler/src/diagnostics/codes.rs +++ b/crates/move-compiler/src/diagnostics/codes.rs @@ -240,6 +240,7 @@ codes!( InvalidTypeParameter: { msg: "invalid type parameter", severity: NonblockingError }, InvalidPattern: { msg: "invalid pattern", severity: BlockingError }, UnboundVariant: { msg: "unbound variant", severity: BlockingError }, + InvalidTypeAnnotation: { msg: "invalid type annotation", severity: NonblockingError }, ], // errors for typing rules. mostly typing/translate TypeSafety: [ diff --git a/crates/move-compiler/src/editions/mod.rs b/crates/move-compiler/src/editions/mod.rs index 89b8982b1a..d0f6b18c97 100644 --- a/crates/move-compiler/src/editions/mod.rs +++ b/crates/move-compiler/src/editions/mod.rs @@ -49,6 +49,7 @@ pub enum FeatureGate { AutoborrowEq, CleverAssertions, NoParensCast, + TypeHoles, } #[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord, Default)] @@ -131,7 +132,7 @@ pub fn valid_editions_for_feature(feature: FeatureGate) -> Vec { static SUPPORTED_FEATURES: Lazy>> = Lazy::new(|| BTreeMap::from_iter(Edition::ALL.iter().map(|e| (*e, e.features())))); -const E2024_ALPHA_FEATURES: &[FeatureGate] = &[FeatureGate::MacroFuns]; +const E2024_ALPHA_FEATURES: &[FeatureGate] = &[FeatureGate::MacroFuns, FeatureGate::TypeHoles]; const E2024_BETA_FEATURES: &[FeatureGate] = &[ FeatureGate::NestedUse, @@ -271,6 +272,7 @@ impl FeatureGate { FeatureGate::AutoborrowEq => "Automatic borrowing is", FeatureGate::CleverAssertions => "Clever `assert!`, `abort`, and `#[error]` are", FeatureGate::NoParensCast => "'as' without parentheses is", + FeatureGate::TypeHoles => "'_' placeholders for type inference are", } } } diff --git a/crates/move-compiler/src/expansion/translate.rs b/crates/move-compiler/src/expansion/translate.rs index 348a18ac8b..c11f79d194 100644 --- a/crates/move-compiler/src/expansion/translate.rs +++ b/crates/move-compiler/src/expansion/translate.rs @@ -3390,8 +3390,7 @@ fn lvalues(context: &mut Context, e: Box) -> Option { } fn assign(context: &mut Context, sp!(loc, e_): P::Exp) -> Option { - use E::LValue_ as EL; - use E::ModuleAccess_ as M; + use E::{LValue_ as EL, ModuleAccess_ as M}; use P::Exp_ as PE; match e_ { PE::Name(name) => { @@ -3765,6 +3764,13 @@ fn check_valid_type_parameter_name( is_macro: Option, n: &Name, ) -> Result<(), ()> { + // TODO move these names to a more central place? + if n.value == symbol!("_") { + let diag = restricted_name_error(NameCase::TypeParameter, n.loc, "_"); + context.env().add_diag(diag); + return Err(()); + } + const SYNTAX_IDENTIFIER_NOTE: &str = "Type parameter names starting with '$' indicate that \ their arguments do not have to satisfy certain constraints before the macro is expanded, \ meaning types like '&mut u64' or '(bool, u8)' may be used as arguments."; @@ -3785,6 +3791,19 @@ fn check_valid_type_parameter_name( ); diag.add_note(SYNTAX_IDENTIFIER_NOTE); context.env().add_diag(diag); + } else { + let next_char = n.value.chars().nth(1).unwrap(); + if !next_char.is_ascii_alphabetic() { + let msg = format!( + "Invalid type parameter name '{}'. \ + Following the '$', the '{} fun' type parameter must be have a valid type \ + parameter name starting with a letter 'a'..'z' or 'A'..'Z'", + n, MACRO_MODIFIER + ); + let mut diag = diag!(Declarations::InvalidName, (n.loc, msg)); + diag.add_note(SYNTAX_IDENTIFIER_NOTE); + context.env().add_diag(diag); + } } } else if is_syntax_ident { let msg = format!( @@ -3798,11 +3817,6 @@ fn check_valid_type_parameter_name( } // TODO move these names to a more central place? - if n.value == symbol!("_") { - let diag = restricted_name_error(NameCase::TypeParameter, n.loc, "_"); - context.env().add_diag(diag); - return Err(()); - } check_restricted_names( context, NameCase::TypeParameter, diff --git a/crates/move-compiler/src/naming/translate.rs b/crates/move-compiler/src/naming/translate.rs index c689226fe1..34870506aa 100644 --- a/crates/move-compiler/src/naming/translate.rs +++ b/crates/move-compiler/src/naming/translate.rs @@ -39,6 +39,7 @@ pub(super) enum ResolvedType { ModuleType(Box), TParam(Loc, N::TParam), BuiltinType(N::BuiltinTypeName_), + Hole, // '_' type Unbound, } @@ -189,6 +190,16 @@ enum NominalBlockType { LambdaLoopCapture, } +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +enum TypeAnnotation { + StructField, + VariantField, + ConstantSignature, + FunctionSignature, + MacroSignature, + Expression, +} + pub(super) struct Context<'env> { pub env: &'env mut CompilationEnv, current_module: Option, @@ -441,6 +452,12 @@ impl<'env> Context<'env> { pub fn resolve_type(&mut self, sp!(nloc, ma_): E::ModuleAccess) -> ResolvedType { use E::ModuleAccess_ as EN; match ma_ { + EN::Name(sp!(_, n)) if n == symbol!("_") => { + let current_package = self.current_package; + self.env + .check_feature(current_package, FeatureGate::TypeHoles, nloc); + ResolvedType::Hole + } EN::Name(n) => self.resolve_unscoped_type(nloc, n), EN::ModuleAccess(m, n) | EN::Variant(sp!(_, (m, n)), _) => { let Some(module_type) = self.resolve_module_type(nloc, &m, &n) else { @@ -504,7 +521,9 @@ impl<'env> Context<'env> { assert!(self.env.has_errors()); None } - rt @ (ResolvedType::BuiltinType(_) | ResolvedType::TParam(_, _)) => { + rt @ (ResolvedType::BuiltinType(_) + | ResolvedType::TParam(_, _) + | ResolvedType::Hole) => { let (rtloc, msg) = match rt { ResolvedType::TParam(loc, tp) => ( loc, @@ -516,6 +535,10 @@ impl<'env> Context<'env> { ResolvedType::BuiltinType(n) => { (ma.loc, format!("But '{n}' is a builtin type")) } + ResolvedType::Hole => ( + ma.loc, + "The '_' is a placeholder for type inference".to_owned(), + ), _ => unreachable!(), }; self.env.add_diag(diag!( @@ -535,7 +558,7 @@ impl<'env> Context<'env> { ModuleType::Struct(struct_type) => { let m = struct_type.original_mident; let tys_opt = etys_opt.map(|etys| { - let tys = types(self, etys); + let tys = types(self, TypeAnnotation::Expression, etys); let name_f = || format!("{}::{}", &m, &n); check_type_argument_arity(self, loc, name_f, tys, struct_type.arity) }); @@ -571,7 +594,9 @@ impl<'env> Context<'env> { assert!(self.env.has_errors()); None } - rt @ (ResolvedType::BuiltinType(_) | ResolvedType::TParam(_, _)) => { + rt @ (ResolvedType::BuiltinType(_) + | ResolvedType::TParam(_, _) + | ResolvedType::Hole) => { let (rtloc, msg) = match rt { ResolvedType::TParam(loc, tp) => ( loc, @@ -583,6 +608,10 @@ impl<'env> Context<'env> { ResolvedType::BuiltinType(n) => { (ma.loc, format!("But '{n}' is a builtin type")) } + ResolvedType::Hole => ( + ma.loc, + "The '_' is a placeholder for type inference".to_owned(), + ), _ => unreachable!(), }; self.env.add_diag(diag!( @@ -602,7 +631,7 @@ impl<'env> Context<'env> { ModuleType::Enum(enum_type) => { let m = enum_type.original_mident; let tys_opt = etys_opt.map(|etys| { - let tys = types(self, etys); + let tys = types(self, TypeAnnotation::Expression, etys); let name_f = || format!("{}::{}", &m, &n); check_type_argument_arity(self, loc, name_f, tys, enum_type.arity) }); @@ -1171,22 +1200,31 @@ fn explicit_use_fun( None } }; + let ty_loc = ty.loc; let tn_opt = match context.resolve_type(ty) { ResolvedType::Unbound => { assert!(context.env.has_errors()); None } + ResolvedType::Hole => { + let msg = "Invalid 'use fun'. Cannot associate a method with an inferred type"; + let tmsg = "The '_' type is a placeholder for type inference"; + context.env.add_diag(diag!( + Declarations::InvalidUseFun, + (loc, msg), + (ty_loc, tmsg) + )); + None + } ResolvedType::TParam(tloc, tp) => { + let msg = "Invalid 'use fun'. Cannot associate a method with a type parameter"; let tmsg = format!( "But '{}' was declared as a type parameter here", tp.user_specified_name ); context.env.add_diag(diag!( Declarations::InvalidUseFun, - ( - loc, - "Invalid 'use fun'. Cannot associate a method with a type parameter" - ), + (loc, msg,), (tloc, tmsg) )); None @@ -1363,7 +1401,12 @@ fn function( context.local_scopes = vec![BTreeMap::new()]; context.local_count = BTreeMap::new(); context.translating_fun = true; - let signature = function_signature(context, signature); + let case = if macro_.is_some() { + TypeAnnotation::MacroSignature + } else { + TypeAnnotation::FunctionSignature + }; + let signature = function_signature(context, case, signature); let body = function_body(context, body); if !matches!(body.value, N::FunctionBody_::Native) { @@ -1402,7 +1445,11 @@ fn function( f } -fn function_signature(context: &mut Context, sig: E::FunctionSignature) -> N::FunctionSignature { +fn function_signature( + context: &mut Context, + case: TypeAnnotation, + sig: E::FunctionSignature, +) -> N::FunctionSignature { let type_parameters = fun_type_parameters(context, sig.type_parameters); let mut declared = UniqueMap::new(); @@ -1440,11 +1487,11 @@ fn function_signature(context: &mut Context, sig: E::FunctionSignature) -> N::Fu } let is_parameter = true; let nparam = context.declare_local(is_parameter, param.0); - let nparam_ty = type_(context, param_ty); + let nparam_ty = type_(context, case, param_ty); (mut_, nparam, nparam_ty) }) .collect(); - let return_type = type_(context, sig.return_type); + let return_type = type_(context, case, sig.return_type); N::FunctionSignature { type_parameters, parameters, @@ -1501,13 +1548,13 @@ fn positional_field_name(loc: Loc, idx: usize) -> Field { fn struct_fields(context: &mut Context, efields: E::StructFields) -> N::StructFields { match efields { E::StructFields::Native(loc) => N::StructFields::Native(loc), - E::StructFields::Named(em) => { - N::StructFields::Defined(em.map(|_f, (idx, t)| (idx, type_(context, t)))) - } + E::StructFields::Named(em) => N::StructFields::Defined( + em.map(|_f, (idx, t)| (idx, type_(context, TypeAnnotation::StructField, t))), + ), E::StructFields::Positional(tys) => { let fields = tys .into_iter() - .map(|ty| type_(context, ty)) + .map(|ty| type_(context, TypeAnnotation::StructField, ty)) .enumerate() .map(|(idx, ty)| { let field_name = positional_field_name(ty.loc, idx); @@ -1573,13 +1620,14 @@ fn variant_def(context: &mut Context, variant: E::VariantDefinition) -> N::Varia fn variant_fields(context: &mut Context, efields: E::VariantFields) -> N::VariantFields { match efields { E::VariantFields::Empty => N::VariantFields::Empty, - E::VariantFields::Named(em) => { - N::VariantFields::Defined(false, em.map(|_f, (idx, t)| (idx, type_(context, t)))) - } + E::VariantFields::Named(em) => N::VariantFields::Defined( + false, + em.map(|_f, (idx, t)| (idx, type_(context, TypeAnnotation::VariantField, t))), + ), E::VariantFields::Positional(tys) => { let fields = tys .into_iter() - .map(|ty| type_(context, ty)) + .map(|ty| type_(context, TypeAnnotation::VariantField, ty)) .enumerate() .map(|(idx, ty)| { let field_name = positional_field_name(ty.loc, idx); @@ -1608,7 +1656,7 @@ fn constant(context: &mut Context, _name: ConstantName, econstant: E::Constant) assert!(context.used_locals.is_empty()); context.env.add_warning_filter_scope(warning_filter.clone()); context.local_scopes = vec![BTreeMap::new()]; - let signature = type_(context, esignature); + let signature = type_(context, TypeAnnotation::ConstantSignature, esignature); let value = *exp(context, Box::new(evalue)); context.local_scopes = vec![]; context.local_count = BTreeMap::new(); @@ -1681,20 +1729,21 @@ fn type_parameter( tp } -fn types(context: &mut Context, tys: Vec) -> Vec { - tys.into_iter().map(|t| type_(context, t)).collect() +fn types(context: &mut Context, case: TypeAnnotation, tys: Vec) -> Vec { + tys.into_iter().map(|t| type_(context, case, t)).collect() } -fn type_(context: &mut Context, sp!(loc, ety_): E::Type) -> N::Type { +fn type_(context: &mut Context, case: TypeAnnotation, sp!(loc, ety_): E::Type) -> N::Type { use ResolvedType as RT; use E::Type_ as ET; use N::{TypeName_ as NN, Type_ as NT}; let ty_ = match ety_ { ET::Unit => NT::Unit, - ET::Multiple(tys) => { - NT::multiple_(loc, tys.into_iter().map(|t| type_(context, t)).collect()) - } - ET::Ref(mut_, inner) => NT::Ref(mut_, Box::new(type_(context, *inner))), + ET::Multiple(tys) => NT::multiple_( + loc, + tys.into_iter().map(|t| type_(context, case, t)).collect(), + ), + ET::Ref(mut_, inner) => NT::Ref(mut_, Box::new(type_(context, case, *inner))), ET::UnresolvedError => { assert!(context.env.has_errors()); NT::UnresolvedError @@ -1704,10 +1753,41 @@ fn type_(context: &mut Context, sp!(loc, ety_): E::Type) -> N::Type { assert!(context.env.has_errors()); NT::UnresolvedError } + RT::Hole => { + let case_str_opt = match case { + TypeAnnotation::StructField => { + Some(("Struct fields", " or consider adding a new type parameter")) + } + TypeAnnotation::VariantField => Some(( + "Enum variant fields", + " or consider adding a new type parameter", + )), + TypeAnnotation::ConstantSignature => Some(("Constants", "")), + TypeAnnotation::FunctionSignature => { + Some(("Functions", " or consider adding a new type parameter")) + } + TypeAnnotation::MacroSignature | TypeAnnotation::Expression => None, + }; + if let Some((case_str, help_str)) = case_str_opt { + let msg = format!( + "Invalid usage of a placeholder for type inference '_'. \ + {case_str} require fully specified types. Replace '_' with a specific type{help_str}" + ); + let mut diag = diag!(NameResolution::InvalidTypeAnnotation, (loc, msg)); + if let TypeAnnotation::FunctionSignature = case { + diag.add_note("Only 'macro' functions can use '_' in their signatures"); + } + context.env.add_diag(diag); + NT::UnresolvedError + } else { + // replaced with a type variable during type instantiation + NT::Anything + } + } RT::BuiltinType(bn_) => { let name_f = || format!("{}", &bn_); let arity = bn_.tparam_constraints(loc).len(); - let tys = types(context, tys); + let tys = types(context, case, tys); let tys = check_type_argument_arity(context, loc, name_f, tys, arity); NT::builtin_(sp(ma.loc, bn_), tys) } @@ -1749,15 +1829,15 @@ fn type_(context: &mut Context, sp!(loc, ety_): E::Type) -> N::Type { (tn, arity) } }; - let tys = types(context, tys); + let tys = types(context, case, tys); let name_f = || format!("{}", tn); let tys = check_type_argument_arity(context, loc, name_f, tys, arity); NT::Apply(None, tn, tys) } }, ET::Fun(tys, ty) => { - let tys = types(context, tys); - let ty = Box::new(type_(context, *ty)); + let tys = types(context, case, tys); + let ty = Box::new(type_(context, case, *ty)); NT::Fun(tys, ty) } }; @@ -1820,7 +1900,7 @@ fn sequence_item(context: &mut Context, sp!(loc, ns_): E::SequenceItem) -> N::Se ES::Seq(e) => NS::Seq(exp(context, e)), ES::Declare(b, ty_opt) => { let bind_opt = bind_list(context, b); - let tys = ty_opt.map(|t| type_(context, t)); + let tys = ty_opt.map(|t| type_(context, TypeAnnotation::Expression, t)); match bind_opt { None => { assert!(context.env.has_errors()); @@ -1964,7 +2044,7 @@ fn exp(context: &mut Context, e: Box) -> Box { EE::Lambda(elambda_binds, ety_opt, body) => { context.new_local_scope(); let nlambda_binds_opt = lambda_bind_list(context, elambda_binds); - let return_type = ety_opt.map(|t| type_(context, t)); + let return_type = ety_opt.map(|t| type_(context, TypeAnnotation::Expression, t)); context.enter_nominal_block(eloc, None, NominalBlockType::LambdaLoopCapture); context.enter_nominal_block(eloc, None, NominalBlockType::LambdaReturn); let body = exp(context, body); @@ -2153,8 +2233,14 @@ fn exp(context: &mut Context, e: Box) -> Box { Some(ndot) => NE::ExpDotted(case, ndot), }, - EE::Cast(e, t) => NE::Cast(exp(context, e), type_(context, t)), - EE::Annotate(e, t) => NE::Annotate(exp(context, e), type_(context, t)), + EE::Cast(e, t) => NE::Cast( + exp(context, e), + type_(context, TypeAnnotation::Expression, t), + ), + EE::Annotate(e, t) => NE::Annotate( + exp(context, e), + type_(context, TypeAnnotation::Expression, t), + ), EE::Call(ma, is_macro, tys_opt, rhs) if context.resolves_to_struct(&ma) => { context @@ -2244,7 +2330,7 @@ fn exp(context: &mut Context, e: Box) -> Box { } EE::Call(ma, is_macro, tys_opt, rhs) => { use N::BuiltinFunction_ as BF; - let ty_args = tys_opt.map(|tys| types(context, tys)); + let ty_args = tys_opt.map(|tys| types(context, TypeAnnotation::Expression, tys)); let mut nes = call_args(context, rhs); match resolve_function(context, ResolveFunctionCase::Call, eloc, ma, ty_args) { ResolvedFunction::Builtin(sp!(bloc, BF::Assert(_))) => { @@ -2329,7 +2415,7 @@ fn exp(context: &mut Context, e: Box) -> Box { NE::UnresolvedError } Some(d) => { - let ty_args = tys_opt.map(|tys| types(context, tys)); + let ty_args = tys_opt.map(|tys| types(context, TypeAnnotation::Expression, tys)); let nes = call_args(context, rhs); if is_macro.is_some() { context.env.check_feature( @@ -2342,7 +2428,7 @@ fn exp(context: &mut Context, e: Box) -> Box { } }, EE::Vector(vec_loc, tys_opt, rhs) => { - let ty_args = tys_opt.map(|tys| types(context, tys)); + let ty_args = tys_opt.map(|tys| types(context, TypeAnnotation::Expression, tys)); let nes = call_args(context, rhs); let ty_opt = check_builtin_ty_args_impl( context, @@ -3084,7 +3170,7 @@ fn lambda_bind_list( .into_iter() .map(|(pbs, ty_opt)| { let bs = bind_list(context, pbs)?; - let ety = ty_opt.map(|t| type_(context, t)); + let ety = ty_opt.map(|t| type_(context, TypeAnnotation::Expression, t)); Some((bs, ety)) }) .collect::>()?; diff --git a/crates/move-compiler/src/typing/core.rs b/crates/move-compiler/src/typing/core.rs index 2300a95936..1b27dec22b 100644 --- a/crates/move-compiler/src/typing/core.rs +++ b/crates/move-compiler/src/typing/core.rs @@ -1831,12 +1831,17 @@ pub fn all_tparams(sp!(_, t_): Type) -> BTreeSet { pub fn ready_tvars(subst: &Subst, sp!(loc, t_): Type) -> Type { use Type_::*; match t_ { - x @ UnresolvedError | x @ Unit | x @ Anything | x @ Param(_) => sp(loc, x), + x @ (UnresolvedError | Unit | Anything | Param(_)) => sp(loc, x), Ref(mut_, t) => sp(loc, Ref(mut_, Box::new(ready_tvars(subst, *t)))), Apply(k, n, tys) => { let tys = tys.into_iter().map(|t| ready_tvars(subst, t)).collect(); sp(loc, Apply(k, n, tys)) } + Fun(args, result) => { + let args = args.into_iter().map(|t| ready_tvars(subst, t)).collect(); + let result = Box::new(ready_tvars(subst, *result)); + sp(loc, Fun(args, result)) + } Var(i) => { let last_var = forward_tvar(subst, i); match subst.get(last_var) { @@ -1845,11 +1850,6 @@ pub fn ready_tvars(subst: &Subst, sp!(loc, t_): Type) -> Type { Some(t) => ready_tvars(subst, t.clone()), } } - Fun(args, result) => { - let args = args.into_iter().map(|t| ready_tvars(subst, t)).collect(); - let result = Box::new(ready_tvars(subst, *result)); - sp(loc, Fun(args, result)) - } } } @@ -1857,23 +1857,71 @@ pub fn ready_tvars(subst: &Subst, sp!(loc, t_): Type) -> Type { // Instantiate //************************************************************************************************** -pub fn instantiate(context: &mut Context, sp!(loc, t_): Type) -> Type { +pub fn instantiate(context: &mut Context, ty: Type) -> Type { + let keep_tanything = false; + instantiate_impl(context, keep_tanything, ty) +} + +pub fn instantiate_keep_tanything(context: &mut Context, ty: Type) -> Type { + let keep_tanything = true; + instantiate_impl(context, keep_tanything, ty) +} + +fn instantiate_apply( + context: &mut Context, + loc: Loc, + abilities_opt: Option, + n: TypeName, + ty_args: Vec, +) -> Type_ { + let keep_tanything = false; + instantiate_apply_impl(context, keep_tanything, loc, abilities_opt, n, ty_args) +} + +fn instantiate_type_args( + context: &mut Context, + loc: Loc, + case: TArgCase, + ty_args: Vec, + constraints: Vec, +) -> Vec { + let keep_tanything = false; + instantiate_type_args_impl(context, keep_tanything, loc, case, ty_args, constraints) +} + +/// Instantiates a type, applying constraints to type arguments, and binding type arguments to +/// type variables +/// keep_tanything is an annoying case to handle macro signature checking, were we want to delay +/// instantiating Anything to a type varabile until _after_ the macro is expanded +fn instantiate_impl(context: &mut Context, keep_tanything: bool, sp!(loc, t_): Type) -> Type { use Type_::*; let it_ = match t_ { Unit => Unit, UnresolvedError => UnresolvedError, - Anything => make_tvar(context, loc).value, + Anything => { + if keep_tanything { + Anything + } else { + make_tvar(context, loc).value + } + } + Ref(mut_, b) => { let inner = *b; context.add_base_type_constraint(loc, "Invalid reference type", inner.clone()); - Ref(mut_, Box::new(instantiate(context, inner))) + Ref( + mut_, + Box::new(instantiate_impl(context, keep_tanything, inner)), + ) } Apply(abilities_opt, n, ty_args) => { - instantiate_apply(context, loc, abilities_opt, n, ty_args) + instantiate_apply_impl(context, keep_tanything, loc, abilities_opt, n, ty_args) } Fun(args, result) => Fun( - args.into_iter().map(|t| instantiate(context, t)).collect(), - Box::new(instantiate(context, *result)), + args.into_iter() + .map(|t| instantiate_impl(context, keep_tanything, t)) + .collect(), + Box::new(instantiate_impl(context, keep_tanything, *result)), ), x @ Param(_) => x, // instantiating a var really shouldn't happen... but it does because of macro expansion @@ -1887,8 +1935,9 @@ pub fn instantiate(context: &mut Context, sp!(loc, t_): Type) -> Type { } // abilities_opt is expected to be None for non primitive types -fn instantiate_apply( +fn instantiate_apply_impl( context: &mut Context, + keep_tanything: bool, loc: Loc, abilities_opt: Option, n: TypeName, @@ -1910,8 +1959,9 @@ fn instantiate_apply( } }; - let tys = instantiate_type_args( + let tys = instantiate_type_args_impl( context, + keep_tanything, loc, TArgCase::Apply(&n.value), ty_args, @@ -1925,8 +1975,9 @@ fn instantiate_apply( // This might be needed for any variance case, and I THINK that it should be fine without it // BUT I'm adding it as a safeguard against instantiating twice. Can always remove once this // stabilizes -fn instantiate_type_args( +fn instantiate_type_args_impl( context: &mut Context, + keep_tanything: bool, loc: Loc, case: TArgCase, mut ty_args: Vec, @@ -1947,10 +1998,12 @@ fn instantiate_type_args( | TArgCase::Apply(TypeName_::ModuleType(_, _)) => TVarCase::Base, TArgCase::Macro => TVarCase::Macro, }; + // TODO in many cases we likely immediatley fill these type variables in with the type + // arguments. We could maybe just not create them in the first place in some instances let tvars = make_tparams(context, loc, tvar_case, locs_constraints); ty_args = ty_args .into_iter() - .map(|t| instantiate(context, t)) + .map(|t| instantiate_impl(context, keep_tanything, t)) .collect(); assert!(ty_args.len() == tvars.len()); diff --git a/crates/move-compiler/src/typing/translate.rs b/crates/move-compiler/src/typing/translate.rs index 14c0ec7dc6..2f84be5a4f 100644 --- a/crates/move-compiler/src/typing/translate.rs +++ b/crates/move-compiler/src/typing/translate.rs @@ -30,8 +30,7 @@ use crate::{ typing::{ ast as T, core::{ - self, make_tvar, public_testing_visibility, Context, PublicForTesting, - ResolvedFunctionType, Subst, + self, public_testing_visibility, Context, PublicForTesting, ResolvedFunctionType, Subst, }, dependency_ordering, expand, infinite_instantiations, macro_expand, recursive_datatypes, syntax_methods::validate_syntax_methods, @@ -4091,16 +4090,16 @@ fn expected_by_name_arg_type( .iter() .map(|(p, ty_opt)| { if let Some(ty) = ty_opt { - core::instantiate(context, ty.clone()) + core::instantiate_keep_tanything(context, ty.clone()) } else { - core::make_tvar(context, p.loc) + sp(p.loc, Type_::Anything) } }) .collect(); let ret_ty = if let Some(ty) = lambda.return_type.clone() { - core::instantiate(context, ty) + core::instantiate_keep_tanything(context, ty) } else { - make_tvar(context, lambda.body.loc) + sp(lambda.body.loc, Type_::Anything) }; let tfun = sp(eloc, Type_::Fun(param_tys, Box::new(ret_ty))); let msg = || { @@ -4109,9 +4108,14 @@ fn expected_by_name_arg_type( m, &f, ¶m.value.name ) }; - subtype(context, call_loc, msg, tfun.clone(), param_ty); - // prefer the lambda type over the parameters to preserve annotations on the lambda - tfun + // We need to return the subtyped type to properly remove the `Anything` in the cases + // where it should be a specific type, e.g. |_| -> _ <: |'a| -> 'b should return |'a| -> 'b + // In the case of an error, we give back tfun so macro expansion continues to know that this + // argument is a lambda + match subtype_impl(context, call_loc, msg, tfun.clone(), param_ty) { + Ok(t) => t, + Err(_) => tfun, + } } fn expand_macro( @@ -4123,8 +4127,7 @@ fn expand_macro( args: Vec, return_ty: Type, ) -> (Type, T::UnannotatedExp_) { - use T::SequenceItem_ as TS; - use T::UnannotatedExp_ as TE; + use T::{SequenceItem_ as TS, UnannotatedExp_ as TE}; let valid = context.add_macro_expansion(m, f, call_loc); if !valid { diff --git a/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.exp b/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.exp new file mode 100644 index 0000000000..414d4ff5ed --- /dev/null +++ b/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.exp @@ -0,0 +1,24 @@ +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_invalid_type_parameter.move:3:19 + │ +3 │ macro fun foo<$_, $__, $_t>() {} + │ ^^ Invalid type parameter name '$_'. Following the '$', the 'macro fun' type parameter must be have a valid type parameter name starting with a letter 'a'..'z' or 'A'..'Z' + │ + = Type parameter names starting with '$' indicate that their arguments do not have to satisfy certain constraints before the macro is expanded, meaning types like '&mut u64' or '(bool, u8)' may be used as arguments. + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_invalid_type_parameter.move:3:23 + │ +3 │ macro fun foo<$_, $__, $_t>() {} + │ ^^^ Invalid type parameter name '$__'. Following the '$', the 'macro fun' type parameter must be have a valid type parameter name starting with a letter 'a'..'z' or 'A'..'Z' + │ + = Type parameter names starting with '$' indicate that their arguments do not have to satisfy certain constraints before the macro is expanded, meaning types like '&mut u64' or '(bool, u8)' may be used as arguments. + +error[E02010]: invalid name + ┌─ tests/move_2024/expansion/macro_identifier_invalid_type_parameter.move:3:28 + │ +3 │ macro fun foo<$_, $__, $_t>() {} + │ ^^^ Invalid type parameter name '$_t'. Following the '$', the 'macro fun' type parameter must be have a valid type parameter name starting with a letter 'a'..'z' or 'A'..'Z' + │ + = Type parameter names starting with '$' indicate that their arguments do not have to satisfy certain constraints before the macro is expanded, meaning types like '&mut u64' or '(bool, u8)' may be used as arguments. + diff --git a/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.move b/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.move new file mode 100644 index 0000000000..f11d81ea72 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/expansion/macro_identifier_invalid_type_parameter.move @@ -0,0 +1,4 @@ +module a::m { + // invalid type parameter + macro fun foo<$_, $__, $_t>() {} +} diff --git a/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move b/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move index 2eff747f7d..da45f6ab3a 100644 --- a/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move +++ b/crates/move-compiler/tests/move_2024/expansion/macro_identifier_valid_names.move @@ -1,6 +1,6 @@ module a::m { // TODO should we allow all of these? - macro fun foo<$T, $x, $_>( + macro fun foo<$T, $x>( _: u64, $_: u64, $f: |u64| -> u64, diff --git a/crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.exp b/crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.exp new file mode 100644 index 0000000000..803ff99dde --- /dev/null +++ b/crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.exp @@ -0,0 +1,26 @@ +error[E03011]: invalid use of reserved name + ┌─ tests/move_2024/expansion/type_hole_as_type_param.move:2:21 + │ +2 │ public struct S<_>() + │ ^ Invalid type parameter name '_'. '_' is restricted and cannot be used to name a type parameter + +warning[W09006]: unused struct type parameter + ┌─ tests/move_2024/expansion/type_hole_as_type_param.move:2:21 + │ +2 │ public struct S<_>() + │ ^ Unused type parameter '_'. Consider declaring it as phantom + │ + = This warning can be suppressed with '#[allow(unused_type_parameter)]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +error[E03011]: invalid use of reserved name + ┌─ tests/move_2024/expansion/type_hole_as_type_param.move:3:13 + │ +3 │ fun foo<_>() {} + │ ^ Invalid type parameter name '_'. '_' is restricted and cannot be used to name a type parameter + +error[E03011]: invalid use of reserved name + ┌─ tests/move_2024/expansion/type_hole_as_type_param.move:4:19 + │ +4 │ macro fun bar<_>() {} + │ ^ Invalid type parameter name '_'. '_' is restricted and cannot be used to name a type parameter + diff --git a/crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.move b/crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.move new file mode 100644 index 0000000000..b8507bc729 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/expansion/type_hole_as_type_param.move @@ -0,0 +1,5 @@ +module a::m { + public struct S<_>() + fun foo<_>() {} + macro fun bar<_>() {} +} diff --git a/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp b/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp index d436323f04..b4a0fddaf5 100644 --- a/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp +++ b/crates/move-compiler/tests/move_2024/naming/macro_identifier_assignment.exp @@ -1,3 +1,15 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/naming/macro_identifier_assignment.move:2:33 + │ + 2 │ macro fun call($f: |u64| -> u64, $x: u64): u64 { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: 'u64' + · +10 │ call!(|_| false, 0) + 1; + │ ----- Given: 'bool' + error[E04032]: unable to expand macro function ┌─ tests/move_2024/naming/macro_identifier_assignment.move:3:9 │ @@ -14,15 +26,3 @@ error[E04032]: unable to expand macro function │ = 'macro' parameters are substituted without being evaluated. There is no local variable to assign to -error[E04007]: incompatible types - ┌─ tests/move_2024/naming/macro_identifier_assignment.move:10:19 - │ - 2 │ macro fun call($f: |u64| -> u64, $x: u64): u64 { - │ --- Expected: 'u64' - · -10 │ call!(|_| false, 0) + 1; - │ ^^^^^ - │ │ - │ Invalid type annotation - │ Given: 'bool' - diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.exp b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.exp new file mode 100644 index 0000000000..3178054a65 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.exp @@ -0,0 +1,54 @@ +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:2:21 + │ +2 │ public struct P(_) + │ ^ Invalid usage of a placeholder for type inference '_'. Struct fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:3:26 + │ +3 │ public struct S { f: _ } + │ ^ Invalid usage of a placeholder for type inference '_'. Struct fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:4:5 + │ +4 │ public enum E { P(_), S { f: _ } } + │ ^^^^^^ Enums are under development and should not be used right now. + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:4:23 + │ +4 │ public enum E { P(_), S { f: _ } } + │ ^ Invalid usage of a placeholder for type inference '_'. Enum variant fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:4:34 + │ +4 │ public enum E { P(_), S { f: _ } } + │ ^ Invalid usage of a placeholder for type inference '_'. Enum variant fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:5:14 + │ +5 │ const C: _ = 0; + │ ^ Invalid usage of a placeholder for type inference '_'. Constants require fully specified types. Replace '_' with a specific type + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:6:16 + │ +6 │ fun foo(_: _) {} + │ ^ Invalid usage of a placeholder for type inference '_'. Functions require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + │ + = Only 'macro' functions can use '_' in their signatures + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location.move:7:16 + │ +7 │ fun bar(): _ { 0 } + │ ^ Invalid usage of a placeholder for type inference '_'. Functions require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + │ + = Only 'macro' functions can use '_' in their signatures + diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.move b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.move new file mode 100644 index 0000000000..4cd7cb628a --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location.move @@ -0,0 +1,8 @@ +module a::m { + public struct P(_) + public struct S { f: _ } + public enum E { P(_), S { f: _ } } + const C: _ = 0; + fun foo(_: _) {} + fun bar(): _ { 0 } +} diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.exp b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.exp new file mode 100644 index 0000000000..1915c9f6e6 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.exp @@ -0,0 +1,68 @@ +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:3:31 + │ +3 │ public struct P(Pair) + │ ^ Invalid usage of a placeholder for type inference '_'. Struct fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:4:31 + │ +4 │ public struct S { f: Pair<_, bool> } + │ ^ Invalid usage of a placeholder for type inference '_'. Struct fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:5:5 + │ +5 │ public enum E { P(Pair<_, _>), S { f: vector<_> } } + │ ^^^^^^ Enums are under development and should not be used right now. + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:5:28 + │ +5 │ public enum E { P(Pair<_, _>), S { f: vector<_> } } + │ ^ Invalid usage of a placeholder for type inference '_'. Enum variant fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:5:31 + │ +5 │ public enum E { P(Pair<_, _>), S { f: vector<_> } } + │ ^ Invalid usage of a placeholder for type inference '_'. Enum variant fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:5:50 + │ +5 │ public enum E { P(Pair<_, _>), S { f: vector<_> } } + │ ^ Invalid usage of a placeholder for type inference '_'. Enum variant fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:6:21 + │ +6 │ const C: vector<_> = vector[0]; + │ ^ Invalid usage of a placeholder for type inference '_'. Constants require fully specified types. Replace '_' with a specific type + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:7:21 + │ +7 │ fun foo(p: Pair<_, vector>): vector { p.1 } + │ ^ Invalid usage of a placeholder for type inference '_'. Functions require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + │ + = Only 'macro' functions can use '_' in their signatures + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:8:21 + │ +8 │ fun bar(): Pair<_, _> { Pair(any(), any()) } + │ ^ Invalid usage of a placeholder for type inference '_'. Functions require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + │ + = Only 'macro' functions can use '_' in their signatures + +error[E03021]: invalid type annotation + ┌─ tests/move_2024/naming/type_hole_invalid_location_nested.move:8:24 + │ +8 │ fun bar(): Pair<_, _> { Pair(any(), any()) } + │ ^ Invalid usage of a placeholder for type inference '_'. Functions require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + │ + = Only 'macro' functions can use '_' in their signatures + diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.move b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.move new file mode 100644 index 0000000000..3527757e74 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_invalid_location_nested.move @@ -0,0 +1,11 @@ +module a::m { + public struct Pair(T1, T2) has copy, drop, store; + public struct P(Pair) + public struct S { f: Pair<_, bool> } + public enum E { P(Pair<_, _>), S { f: vector<_> } } + const C: vector<_> = vector[0]; + fun foo(p: Pair<_, vector>): vector { p.1 } + fun bar(): Pair<_, _> { Pair(any(), any()) } + + fun any(): T { abort 0 } +} diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location.move b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location.move new file mode 100644 index 0000000000..adf13abc2c --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location.move @@ -0,0 +1,37 @@ +// valid locations for _ +module a::m { + public struct P(T) + public struct S { x: T } + macro fun foo($x: _, $y: vector<_>): (_, &_) { + $x; + $y; + abort 0 + } + + fun t() { + let mut x; + let P<_>(_) = P<_>(0); + P<_>(x) = P<_>(0); + x; + let S<_> { x: _ } = S<_> { x: 0 }; + S<_> { x } = S<_> { x: 0 }; + x; + + let y: _; + y = 0; + y; + let v: vector<_>; + v = vector[0]; + v; + (0: _); + (vector[0]: vector<_>); + id<_>(0); + id>(vector<_>[0]); + X().xid<_>(0); + X().xid>(vector<_>[0]); + } + + public struct X() has copy, drop; + fun id(t: T): T { t } + fun xid(_: X, t: T): T { t } +} diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.exp b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.exp new file mode 100644 index 0000000000..6bf206a7aa --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.exp @@ -0,0 +1,14 @@ +error[E04003]: built-in operation not supported + ┌─ tests/move_2024/naming/type_hole_valid_location_invalid_usage.move:6:9 + │ +6 │ 0 as S<_>; + │ ^ ---- Found: 'a::m::S<_>'. But expected: 'u8', 'u16', 'u32', 'u64', 'u128', 'u256' + │ │ + │ Invalid argument to 'as' + +error[E04010]: cannot infer type + ┌─ tests/move_2024/naming/type_hole_valid_location_invalid_usage.move:6:16 + │ +6 │ 0 as S<_>; + │ ^ Could not infer this type. Try adding an annotation + diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.move b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.move new file mode 100644 index 0000000000..2791ab1076 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_invalid_usage.move @@ -0,0 +1,9 @@ +// valid locations for _, but the overall example is incorrect +module a::m { + public struct S has copy, drop { x: T } + + fun t() { + 0 as S<_>; + } + +} diff --git a/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_lambda.move b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_lambda.move new file mode 100644 index 0000000000..716ce1aa12 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/naming/type_hole_valid_location_lambda.move @@ -0,0 +1,11 @@ +module a::m { + macro fun call<$T>($f: |$T| -> $T, $x: $T): $T { + $f($x) + } + + fun t() { + call!<_>(|_| -> _ { 0 }, 1); + call!<_>(|_: _| -> _ { 0 }, 1); + call!>(|_: vector<_>| -> vector<_> { vector<_>[] }, vector[0]); + } +} diff --git a/crates/move-compiler/tests/move_2024/parser/type_hole.move b/crates/move-compiler/tests/move_2024/parser/type_hole.move new file mode 100644 index 0000000000..116e1f2585 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/parser/type_hole.move @@ -0,0 +1,10 @@ +module a::m { + fun id(x: T): T { + x + } + + fun foo(): vector { + let v: vector<_> = id<_>(vector<_>[]); + v + } +} diff --git a/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp b/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp index 465219567c..cd0fba9e9c 100644 --- a/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp +++ b/crates/move-compiler/tests/move_2024/typing/lambda_annotated_return_type_invalid.exp @@ -1,18 +1,20 @@ error[E04007]: incompatible types - ┌─ tests/move_2024/typing/lambda_annotated_return_type_invalid.move:13:21 + ┌─ tests/move_2024/typing/lambda_annotated_return_type_invalid.move:13:9 │ 13 │ call!(|| -> X { 0 }).foo(); - │ ^ - Given: integer - │ │ - │ Invalid type annotation - │ Expected: 'a::m::X' + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Given: integer + │ │ Expected: 'a::m::X' + │ Invalid type annotation error[E04006]: invalid subtype - ┌─ tests/move_2024/typing/lambda_annotated_return_type_invalid.move:14:21 + ┌─ tests/move_2024/typing/lambda_annotated_return_type_invalid.move:14:9 │ 14 │ call!(|| -> &mut u64 { &0 }); - │ ^^^^^^^^ -- Given: '&{integer}' - │ │ - │ Invalid type annotation - │ Expected: '&mut u64' + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Given: '&{integer}' + │ │ Expected: '&mut u64' + │ Invalid type annotation diff --git a/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp b/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp index 671a0e72d3..9027b3146e 100644 --- a/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp +++ b/crates/move-compiler/tests/move_2024/typing/lambda_return_invalid_conditional.exp @@ -12,11 +12,11 @@ error[E04007]: incompatible types ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:8:18 │ 8 │ call!(|| { if (cond) 1 else return &0 }); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ │ - │ │ Found: '&{integer}'. It is not compatible with the other type. - │ Invalid lambda return - │ Found: integer. It is not compatible with the other type. + │ ---------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + │ │ │ │ + │ │ │ Found: '&{integer}'. It is not compatible with the other type. + │ │ Invalid lambda return + │ Found: integer. It is not compatible with the other type. error[E04007]: incompatible types ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:9:18 @@ -32,11 +32,11 @@ error[E04007]: incompatible types ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:10:18 │ 10 │ call!(|| { if (cond) 1 else return &0 }); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ │ - │ │ Found: '&{integer}'. It is not compatible with the other type. - │ Invalid lambda return - │ Found: integer. It is not compatible with the other type. + │ ---------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + │ │ │ │ + │ │ │ Found: '&{integer}'. It is not compatible with the other type. + │ │ Invalid lambda return + │ Found: integer. It is not compatible with the other type. error[E04007]: incompatible types ┌─ tests/move_2024/typing/lambda_return_invalid_conditional.move:11:18 diff --git a/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp b/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp index d97f1fbf23..7693e405d5 100644 --- a/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp +++ b/crates/move-compiler/tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.exp @@ -1,13 +1,14 @@ error[E04006]: invalid subtype ┌─ tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move:15:10 │ -14 │ macro fun return_imm($f: || -> &u64) { - │ ---- Given: '&u64' 15 │ *$f() = 0; │ ^^^^ │ │ │ Invalid mutation. Expected a mutable reference │ Expected: '&mut _' + · +24 │ return_imm!(|| &mut x); + │ ------ Given: '&u64' error[E04006]: invalid subtype ┌─ tests/move_2024/typing/lambda_subtyping_usage_respects_annotations.move:20:24 diff --git a/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp b/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp index fd7befe3e4..4cebd692b9 100644 --- a/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp +++ b/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid.exp @@ -1,35 +1,34 @@ error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:10:17 - │ - 9 │ foo!>( - │ --- ---------- Expected: 'vector' - │ │ - │ Given: 'u64' -10 │ |x| x, // invalid - │ ^ Invalid type annotation + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:9:19 + │ +9 │ foo!>( + │ --- ^^^^^^^^^^ + │ │ │ + │ │ Invalid type annotation + │ │ Expected: 'vector' + │ Given: 'u64' error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:16:20 + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:14:19 │ 14 │ foo!>( - │ --- -- Expected: 'u8' - │ │ + │ --- ^^^^^^^^^^ + │ │ │ │ + │ │ │ Expected: 'u8' + │ │ Invalid type annotation │ Given: 'u64' -15 │ |_| vector[], -16 │ |a, b| vector[a, b], // invalid - │ ^^^^^^^^^^^^ Invalid type annotation error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:22:16 + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:19:19 │ 19 │ foo!>( - │ -- Expected: 'u8' + │ ^^^^^^^^^^ + │ │ │ + │ │ Expected: 'u8' + │ Invalid type annotation · 22 │ || vector[vector[]], // invalid - │ ^^^^^^^^^^^^^^^^ - │ │ │ - │ │ Given: 'vector<_>' - │ Invalid type annotation + │ -------- Given: 'vector<_>' error[E04010]: cannot infer type ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid.move:22:23 diff --git a/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp b/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp index 7f6d5eeff0..045e94db97 100644 --- a/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp +++ b/crates/move-compiler/tests/move_2024/typing/macros_lambdas_checked_invalid_arity.exp @@ -1,3 +1,27 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:2:73 + │ + 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + │ ^^^^^^^^ + │ │ + │ Invalid type annotation + │ Expected expression list of length 2: '(vector, vector)' + · +32 │ || (b"hello", b"world", b"!"), // invalid + │ -------------------------- Given expression list of length 3: '(vector, vector, vector)' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:2:73 + │ + 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { + │ ^^^^^^^^ + │ │ + │ Invalid type annotation + │ Expected: '(vector, vector)' + · +37 │ || b"hello", // invalid + │ -------- Given: 'vector' + error[E04017]: too many arguments ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:3:9 │ @@ -66,15 +90,15 @@ error[E04010]: cannot infer type │ ^^^^^^^^ Could not infer this type. Try adding an annotation error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:20:17 + ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:19:19 │ 19 │ foo!>( - │ ---------- Expected: 'vector' + │ ^^^^^^^^^^ + │ │ + │ Invalid type annotation + │ Expected: 'vector' 20 │ |_| (vector[], vector[]), // invalid - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Invalid type annotation - │ Given: '(vector, vector)' + │ ---------------------------- Given: '(vector, vector)' error[E04007]: incompatible types ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:24:9 @@ -90,27 +114,3 @@ error[E04007]: incompatible types 28 │ │ ); │ ╰─────────^ Invalid call of 'a::m::foo'. Invalid argument for parameter '$g' -error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:32:16 - │ - 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { - │ -------- Expected expression list of length 2: '(vector, vector)' - · -32 │ || (b"hello", b"world", b"!"), // invalid - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Invalid type annotation - │ Given expression list of length 3: '(vector, vector, vector)' - -error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_lambdas_checked_invalid_arity.move:37:16 - │ - 2 │ macro fun foo<$T, $U>($f: |$T| -> $U, $g: |$T, $T| -> $U, $h: || -> ($U, $U)) { - │ -------- Expected: '(vector, vector)' - · -37 │ || b"hello", // invalid - │ ^^^^^^^^ - │ │ - │ Invalid type annotation - │ Given: 'vector' - diff --git a/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp b/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp index 62637a5b89..7f23eb6e0e 100644 --- a/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp +++ b/crates/move-compiler/tests/move_2024/typing/macros_return_checked_invalid.exp @@ -20,7 +20,7 @@ error[E04007]: incompatible types │ Expected: '&u64' · 20 │ ref!(|| &1); - │ --- Given: 'u64' + │ --------------------- Given: 'u64' error[E04007]: incompatible types ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:14:45 @@ -32,7 +32,7 @@ error[E04007]: incompatible types │ Expected: '(u64, u64)' · 21 │ double!(|| (0, 0)); - │ --- Given: 'u64' + │ ---------------------------- Given: 'u64' error[E04007]: incompatible types ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:14:45 @@ -44,35 +44,32 @@ error[E04007]: incompatible types │ Expected: '(u64, u64)' · 22 │ double!(|| 0); - │ --- Given: 'u64' + │ ----------------------- Given: 'u64' error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:20:26 + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:20:13 │ 20 │ ref!(|| &1); - │ --- ^^ - │ │ │ - │ │ Invalid type annotation - │ │ Given: '&{integer}' + │ ^^^ -- Given: '&{integer}' + │ │ + │ Invalid type annotation │ Expected: 'u64' error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:21:29 + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:21:16 │ 21 │ double!(|| (0, 0)); - │ --- ^^^^^^ - │ │ │ - │ │ Invalid type annotation - │ │ Given: '({integer}, {integer})' + │ ^^^ ------ Given: '({integer}, {integer})' + │ │ + │ Invalid type annotation │ Expected: 'u64' error[E04007]: incompatible types - ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:23:36 + ┌─ tests/move_2024/typing/macros_return_checked_invalid.move:23:16 │ 23 │ double!<(u64, u64), u64>(|| (&0, &0)); - │ --- ^^^^^^^^ - │ │ ││ - │ │ │Given: '&{integer}' - │ │ Invalid type annotation - │ Expected: 'u64' + │ ^^^^^^^^^^ -- Given: '&{integer}' + │ ││ + │ │Expected: 'u64' + │ Invalid type annotation diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_macro.move b/crates/move-compiler/tests/move_2024/typing/type_hole_macro.move new file mode 100644 index 0000000000..0547a4fa17 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_macro.move @@ -0,0 +1,34 @@ +module a::m { + // $e is given a new type on each usage... its weird + macro fun weird($e: _): _ { + // RHS usage is always u8 + // LHS usage is always the inferred return type + $e << $e + } + + fun tweird() { + let _: u8 = weird!(1); + + let _: u8 = weird!(1 + 1); + let _: u16 = weird!(1 + 1); + let _: u32 = weird!(1 + 1); + let _: u64 = weird!(1 + 1); + let _: u128 = weird!(1 + 1); + let _: u256 = weird!(1 + 1); + } + + macro fun woah($e: _): (_, _) { + ($e, $e) + } + + fun twoah() { + let (_, _): (u64, u8) = woah!(1); + + let (_, _): (u8, u256) = woah!(1 + 1); + let (_, _): (u16, u128) = woah!(1 + 1); + let (_, _): (u32, u64) = woah!(1 + 1); + let (_, _): (u64, u32) = woah!(1 + 1); + let (_, _): (u128, u16) = woah!(1 + 1); + let (_, _): (u256, u8) = woah!(1 + 1); + } +} diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda.move b/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda.move new file mode 100644 index 0000000000..19709bab0b --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda.move @@ -0,0 +1,25 @@ +module a::m { + public struct P(T) has drop; + public struct S1 has drop { f: u64 } + public struct S2 has drop { f: u64 } + + macro fun apply($vs: _, $f: _): _ { + $f($vs) + } + + macro fun apply_either($cond: bool, $a: _, $b: _, $f: _): _ { + if ($cond) $f($a) else $f($b) + } + + macro fun apply_either2($cond: bool, $a: _, $b: _, $f: |_| -> _): _ { + if ($cond) $f($a) else $f($b) + } + + fun t() { + apply!(1, |x| vector[x]); + apply!(P(1), |P(x)| vector[x]); + apply_either!(true, S1 { f: 0 }, S2 { f: 0 }, |s| vector[s.f]); + apply_either2!(true, S1 { f: 0 }, S2 { f: 0 }, |s| vector[s.f]); + } + +} diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.exp b/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.exp new file mode 100644 index 0000000000..dcedf0cc4e --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.exp @@ -0,0 +1,46 @@ +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move:6:37 + │ + 6 │ macro fun applyu64($f: |u64| -> u64): u64 { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: 'u64' + · +20 │ applyu64!(|_: _| -> _ { 0u8 }); + │ --- Given: 'u8' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move:6:37 + │ + 6 │ macro fun applyu64($f: |u64| -> u64): u64 { + │ ^^^ + │ │ + │ Invalid type annotation + │ Expected: 'u64' + · +21 │ applyu64!(|_: _| -> _ { b"hello" }); + │ -------- Given: 'vector' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move:10:46 + │ +10 │ macro fun applyvu64($f: |vector| -> vector): vector { + │ ^^^^^^^^^^^ + │ │ │ + │ │ Expected: 'u64' + │ Invalid type annotation + · +22 │ applyvu64!(|_: vector<_>| -> vector<_> { b"hello" }); + │ -------- Given: 'u8' + +error[E04007]: incompatible types + ┌─ tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move:23:9 + │ +23 │ applyt!(0u64, |_: vector<_>| -> vector<_> { b"hello" }); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ │ │ + │ │ │ Expected: 'vector<_>' + │ │ Given: 'u64' + │ Invalid type annotation + diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move b/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move new file mode 100644 index 0000000000..e1785a2e55 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_macro_lambda_with_annotaitons.move @@ -0,0 +1,26 @@ +module a::m { + public struct P(T) has drop; + public struct S1 has drop { f: u64 } + public struct S2 has drop { f: u64 } + + macro fun applyu64($f: |u64| -> u64): u64 { + $f(0) + } + + macro fun applyvu64($f: |vector| -> vector): vector { + $f(vector[]) + } + + macro fun applyt<$T>($t: $T, $f: |$T| -> $T): $T { + $f($t) + } + + // each one of these should be rejected + fun t() { + applyu64!(|_: _| -> _ { 0u8 }); + applyu64!(|_: _| -> _ { b"hello" }); + applyvu64!(|_: vector<_>| -> vector<_> { b"hello" }); + applyt!(0u64, |_: vector<_>| -> vector<_> { b"hello" }); + } + +} diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.exp b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.exp new file mode 100644 index 0000000000..eb933e472c --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.exp @@ -0,0 +1,18 @@ +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/type_hole_no_inferrence.move:3:23 + │ +3 │ let _: vector<_> = vector[]; + │ ^ Could not infer this type. Try adding an annotation + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/type_hole_no_inferrence.move:3:28 + │ +3 │ let _: vector<_> = vector[]; + │ ^^^^^^^^ Could not infer this type. Try adding an annotation + +error[E04010]: cannot infer type + ┌─ tests/move_2024/typing/type_hole_no_inferrence.move:4:13 + │ +4 │ any<_>(); + │ ^ Could not infer this type. Try adding an annotation + diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.move b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.move new file mode 100644 index 0000000000..cd093e7778 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence.move @@ -0,0 +1,9 @@ +module a::m { + fun foo() { + let _: vector<_> = vector[]; + any<_>(); + } + + fun any(): T { abort 0 } + +} diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.exp b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.exp new file mode 100644 index 0000000000..3b61691fa9 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.exp @@ -0,0 +1,9 @@ +error[E04023]: invalid method call + ┌─ tests/move_2024/typing/type_hole_no_inferrence_threaded.move:8:13 + │ +6 │ let mut c: Cup<_> = any(); + │ - Unable to infer type for method call. Try annotating this type +7 │ loop { +8 │ c.0.x(); + │ ^^^^^^^ Invalid method call + diff --git a/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.move b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.move new file mode 100644 index 0000000000..fdd7dce654 --- /dev/null +++ b/crates/move-compiler/tests/move_2024/typing/type_hole_no_inferrence_threaded.move @@ -0,0 +1,13 @@ +module a::m { + public struct Cup(T) has copy, drop, store; + public struct X() has copy, drop, store; + fun x(_: &X) {} + fun foo() { + let mut c: Cup<_> = any(); + loop { + c.0.x(); + c = Cup(X()); + } + } + fun any(): T { abort 0 } +} diff --git a/crates/move-compiler/tests/move_check/expansion/type_parameter_underscore.move b/crates/move-compiler/tests/move_check/expansion/type_parameter_underscore.move new file mode 100644 index 0000000000..4cacd94348 --- /dev/null +++ b/crates/move-compiler/tests/move_check/expansion/type_parameter_underscore.move @@ -0,0 +1,4 @@ +module a::m { + // these are allowed... but should they be? + fun foo<__, _T, _0>() {} +} diff --git a/crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.exp b/crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.exp new file mode 100644 index 0000000000..a0cc3bb1c1 --- /dev/null +++ b/crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.exp @@ -0,0 +1,30 @@ +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/naming/type_hole_gated_invalid_usage.move:2:22 + │ +2 │ struct S { f: _ } + │ ^ '_' placeholders for type inference are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E03021]: invalid type annotation + ┌─ tests/move_check/naming/type_hole_gated_invalid_usage.move:2:22 + │ +2 │ struct S { f: _ } + │ ^ Invalid usage of a placeholder for type inference '_'. Struct fields require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/naming/type_hole_gated_invalid_usage.move:3:16 + │ +3 │ fun foo(_: _) {} + │ ^ '_' placeholders for type inference are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + +error[E03021]: invalid type annotation + ┌─ tests/move_check/naming/type_hole_gated_invalid_usage.move:3:16 + │ +3 │ fun foo(_: _) {} + │ ^ Invalid usage of a placeholder for type inference '_'. Functions require fully specified types. Replace '_' with a specific type or consider adding a new type parameter + │ + = Only 'macro' functions can use '_' in their signatures + diff --git a/crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.move b/crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.move new file mode 100644 index 0000000000..e9da2121dd --- /dev/null +++ b/crates/move-compiler/tests/move_check/naming/type_hole_gated_invalid_usage.move @@ -0,0 +1,4 @@ +module a::m { + struct S { f: _ } + fun foo(_: _) {} +} diff --git a/crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.exp b/crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.exp new file mode 100644 index 0000000000..2960a9fc2f --- /dev/null +++ b/crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.exp @@ -0,0 +1,8 @@ +error[E13001]: feature is not supported in specified edition + ┌─ tests/move_check/naming/type_hole_gated_valid_usage.move:3:23 + │ +3 │ let v: vector<_> = vector[0]; + │ ^ '_' placeholders for type inference are not supported by current edition 'legacy', only '2024.alpha' support this feature + │ + = You can update the edition in the 'Move.toml', or via command line flag if invoking the compiler directly. + diff --git a/crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.move b/crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.move new file mode 100644 index 0000000000..908af7967e --- /dev/null +++ b/crates/move-compiler/tests/move_check/naming/type_hole_gated_valid_usage.move @@ -0,0 +1,6 @@ +module a::m { + fun foo(): vector { + let v: vector<_> = vector[0]; + v + } +}