Repository: aptos-labs/aptos-core (language: python)

Issue:
[Bug][compiler-v2] Enum constructors without arguments don't work in expressions
The following does not work as expected:

```
  enum MessageHolder has key, drop {
        Empty,
        Message{
           message: string::String,
        }
    }

   fun expect_value(): MessageHolder {
       MessageHolder::Empty // <--- should work but doesn't, and gives misleading error message
       // MessageHolder::Empty{} // <-- does work
   }
```




Discussion / hints:

Fix patch:
diff --git a/third_party/move/move-model/src/builder/exp_builder.rs b/third_party/move/move-model/src/builder/exp_builder.rs
index 8a7ac190db9ff..7f326237adcc5 100644
--- a/third_party/move/move-model/src/builder/exp_builder.rs
+++ b/third_party/move/move-model/src/builder/exp_builder.rs
@@ -31,7 +31,7 @@ use codespan_reporting::diagnostic::Severity;
 use itertools::Itertools;
 use move_binary_format::file_format::{self, Ability, AbilitySet};
 use move_compiler::{
-    expansion::ast::{self as EA, wild_card},
+    expansion::ast::{self as EA},
     hlir::ast as HA,
     naming::ast as NA,
     parser::ast::{self as PA, CallKind, Field},
@@ -1500,8 +1500,8 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                     )
                 }
             },
-            EA::Exp_::Pack(maccess, generics, fields) => {
-                if let Some(exp) = self.translate_pack(
+            EA::Exp_::Pack(maccess, generics, fields) => self
+                .translate_pack(
                     &loc,
                     maccess,
                     generics,
@@ -1509,12 +1509,8 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                     expected_type,
                     context,
                     false,
-                ) {
-                    exp
-                } else {
-                    self.new_error_exp()
-                }
-            },
+                )
+                .unwrap_or_else(|| self.new_error_exp()),
             EA::Exp_::IfElse(cond, then, else_) => {
                 let try_freeze_if_else = |et: &mut ExpTranslator,
                                           expected_ty: &Type,
@@ -2422,6 +2418,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                 }
             },
             EA::LValue_::PositionalUnpack(maccess, generics, args) => {
+                let expected_type = &self.subs.specialize(expected_type);
                 let Some((struct_id, variant)) = self.translate_constructor_name(
                     expected_type,
                     expected_order,
@@ -2493,7 +2490,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                                 );
                                 let field_name = Field(field_name);
                                 fields
-                                    .add(field_name, (field_offset, wild_card(*arg_loc)))
+                                    .add(field_name, (field_offset, EA::wild_card(*arg_loc)))
                                     .expect("duplicate keys");
                                 remaining -= 1;
                                 field_offset += 1;
@@ -2529,31 +2526,27 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
     ) -> Option<Pattern> {
         // Translate constructor name
         let expected_type = self.subs.specialize(expected_type);
-        let Some((
+        let (
             QualifiedInstId {
                 module_id,
                 id,
                 inst,
             },
             variant,
-        )) = self.translate_constructor_name(
+        ) = self.translate_constructor_name(
             &expected_type,
             expected_order,
             context,
             loc,
             maccess,
             generics,
-        )
-        else {
-            // error reported by `translate_constructor_name`
-            return None;
-        };
+        )?;
+        let struct_name_loc = self.to_loc(&maccess.loc);
         let struct_name = self
             .parent
             .parent
             .get_struct_name(module_id.qualified(id))
             .clone();
-        let struct_name_loc = self.to_loc(&maccess.loc);
         let ref_expected = expected_type.try_reference_kind();
 
         // Process argument list
@@ -2575,7 +2568,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                         } else {
                             field_ty.clone()
                         };
-                        let lvalue = wild_card(dotdot.loc);
+                        let lvalue = EA::wild_card(dotdot.loc);
                         let translated = self.translate_lvalue(
                             &lvalue,
                             &expected_field_ty,
@@ -2654,10 +2647,45 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         generics: &Option<Vec<EA::Type>>,
     ) -> Option<(QualifiedInstId<StructId>, Option<Symbol>)> {
         let expected_type = self.subs.specialize(expected_type).drop_reference();
+
+        let (struct_name, variant, struct_entry) =
+            self.resolve_struct_access(&expected_type, maccess, true)?;
+
+        // Resolve type instantiation.
+        let name_loc = self.to_loc(&maccess.loc);
+        let instantiation = self.make_instantiation_or_report(
+            &name_loc,
+            true,
+            struct_name.symbol,
+            &struct_entry.type_params,
+            generics,
+        )?;
+
+        // Verify type derived from reference with expected type.
+        let struct_id = struct_entry
+            .module_id
+            .qualified_inst(struct_entry.struct_id, instantiation);
+        let ty = struct_id.to_type();
+        let ty = self.check_type_with_order(expected_order, loc, &ty, &expected_type, context);
+        // Convert the unified type back to struct id
+        let mut struct_id = struct_id;
+        if let Type::Struct(_, _, types) = ty {
+            struct_id.inst = types;
+        }
+
+        Some((struct_id, variant))
+    }
+
+    fn resolve_struct_access(
+        &mut self,
+        expected_type: &Type,
+        maccess: &EA::ModuleAccess,
+        report_error: bool,
+    ) -> Option<(QualifiedSymbol, Option<Symbol>, StructEntry)> {
         // Determine whether expected type is known to have variants (at this
         // point during inference). If so, they are used for name resolution.
         let variant_struct_info = if let Type::Struct(mid, sid, _) = expected_type {
-            let entry = self.parent.parent.lookup_struct_entry(mid.qualified(sid));
+            let entry = self.parent.parent.lookup_struct_entry(mid.qualified(*sid));
             if let StructLayout::Variants(variants) = &entry.layout {
                 Some((
                     entry,
@@ -2687,41 +2715,22 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                 let (struct_name, variant) =
                     self.parent.module_access_to_qualified_with_variant(maccess);
                 let struct_name_loc = self.to_loc(&maccess.loc);
-                let struct_entry =
-                    self.get_struct_report_undeclared(&struct_name, &struct_name_loc)?;
+                let struct_entry = if report_error {
+                    self.get_struct_report_undeclared(&struct_name, &struct_name_loc)?
+                } else {
+                    self.parent.parent.struct_table.get(&struct_name).cloned()?
+                };
+
                 (struct_name, variant, struct_entry)
             },
         };
-
-        // Resolve type instantiation.
-        let instantiation = self.make_instantiation_or_report(
-            loc,
-            true,
-            struct_name.symbol,
-            &struct_entry.type_params,
-            generics,
-        )?;
-
-        // If this is a struct variant, check whether it exists.
-        if let Some(v) = variant {
-            if !self.check_variant_declared(&struct_name, &struct_entry, &struct_name_loc, v) {
+        if let Some(variant) = variant.filter(|_| report_error) {
+            if !self.check_variant_declared(&struct_name, &struct_entry, &struct_name_loc, variant)
+            {
                 return None;
             }
         }
-
-        // Verify type derived from reference with expected type.
-        let struct_id = struct_entry
-            .module_id
-            .qualified_inst(struct_entry.struct_id, instantiation);
-        let ty = struct_id.to_type();
-        let ty = self.check_type_with_order(expected_order, loc, &ty, &expected_type, context);
-        // Convert the unified type back to struct id
-        let mut struct_id = struct_id;
-        if let Type::Struct(_, _, types) = ty {
-            struct_id.inst = types;
-        }
-
-        Some((struct_id, variant))
+        Some((struct_name, variant, struct_entry))
     }
 
     fn new_error_pat(&mut self, loc: &Loc) -> Pattern {
@@ -2939,10 +2948,13 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         result
     }
 
-    /// Checks whether the given name can be resolve to a struct.
-    fn can_resolve_to_struct(&self, maccess: &EA::ModuleAccess) -> bool {
-        let (struct_name, _variant) = self.parent.module_access_to_qualified_with_variant(maccess);
-        self.parent.parent.struct_table.contains_key(&struct_name)
+    /// Checks whether the given name can be resolved to a struct or struct variant
+    fn can_resolve_to_struct(&mut self, expected_type: &Type, maccess: &EA::ModuleAccess) -> bool {
+        (maccess.value.is_valid_struct_constant_or_schema_name()
+            || ModuleBuilder::is_variant(maccess))
+            && self
+                .resolve_struct_access(expected_type, maccess, false)
+                .is_some()
     }
 
     fn translate_fun_call_special_cases(
@@ -2964,10 +2976,8 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         }
 
         // handles call of struct/variant with positional fields
-        if maccess.value.is_valid_struct_constant_or_schema_name()
-            && self.can_resolve_to_struct(maccess)
-            || ModuleBuilder::is_variant(maccess)
-        {
+        let expected_type = &self.subs.specialize(expected_type);
+        if self.can_resolve_to_struct(expected_type, maccess) {
             self.check_language_version(loc, "positional fields", LanguageVersion::V2_0);
             // translates StructName(e0, e1, ...) to pack<StructName> { 0: e0, 1: e1, ... }
             let fields: EA::Fields<_> =
@@ -3282,6 +3292,14 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         expected_type: &Type,
         context: &ErrorMessageContext,
     ) -> ExpData {
+        let expected_type = &self.subs.specialize(expected_type);
+        // Try to resolve as argument-less construction of struct variant
+        if self.can_resolve_to_struct(expected_type, maccess) {
+            return self
+                .translate_pack(loc, maccess, type_args, None, expected_type, context, false)
+                .unwrap_or_else(|| self.new_error_exp());
+        }
+
         let global_var_sym = match &maccess.value {
             EA::ModuleAccess_::ModuleAccess(..) => self.parent.module_access_to_qualified(maccess),
             EA::ModuleAccess_::Name(name) => {
@@ -3874,16 +3892,17 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         context: &ErrorMessageContext,
     ) -> ExpData {
         let (exp_ty, exp) = self.translate_exp_free(exp);
+        let exp_ty = self.subs.specialize(&exp_ty);
         let mut variants = vec![];
         let mut struct_id = None;
         for ty in tys {
-            let loc = self.to_loc(&ty.loc);
+            let ty_loc = self.to_loc(&ty.loc);
             if let EA::Type_::Apply(maccess, generics) = &ty.value {
                 if let Some((inferred_struct_id, variant)) = self.translate_constructor_name(
                     &exp_ty,
                     WideningOrder::LeftToRight,
                     context,
-                    &loc,
+                    &ty_loc,
                     maccess,
                     &Some(generics.clone()),
                 ) {
@@ -3894,7 +3913,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                         variants.push(variant);
                     } else {
                         self.error(
-                            &loc,
+                            &ty_loc,
                             &format!(
                                 "expected variant of enum type but found type `{}`",
                                 self.env().display(&inferred_struct_id)
@@ -4518,25 +4537,21 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         expected_positional_constructor: bool,
     ) -> Option<ExpData> {
         // Resolve reference to struct
-        let (struct_name, variant) = self.parent.module_access_to_qualified_with_variant(maccess);
-        let struct_name_loc = self.to_loc(&maccess.loc);
-        let struct_entry = self.get_struct_report_undeclared(&struct_name, &struct_name_loc)?;
-
-        // Resolve type instantiation
-        let instantiation = self.make_instantiation_or_report(
-            &self.to_loc(&maccess.loc),
-            true,
-            struct_name.symbol,
-            &struct_entry.type_params,
+        // Translate constructor name
+        let expected_type = self.subs.specialize(expected_type);
+        let (struct_inst_id, variant) = self.translate_constructor_name(
+            &expected_type,
+            WideningOrder::LeftToRight,
+            context,
+            loc,
+            maccess,
             generics,
         )?;
-
-        // If this is a struct variant, check whether it exists.
-        if let Some(v) = variant {
-            if !self.check_variant_declared(&struct_name, &struct_entry, &struct_name_loc, v) {
-                return None;
-            }
-        }
+        let struct_name = self
+            .parent
+            .parent
+            .get_struct_name(struct_inst_id.to_qualified_id())
+            .clone();
 
         // Process argument list.
         // given pack<S>{ f_p(1): e_1, ... }, where p is a permutation of the fields
@@ -4549,10 +4564,11 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         // is equivalent.
         let mut bindings = BTreeMap::new();
         let mut args = BTreeMap::new();
+        let struct_name_loc = self.to_loc(&maccess.loc);
         let (field_decls, is_positional_constructor) =
             self.get_field_decls_for_pack_unpack(&struct_name, &struct_name_loc, variant)?;
         let field_decls = field_decls.clone();
-        if is_positional_constructor != expected_positional_constructor {
+        if fields.is_some() && is_positional_constructor != expected_positional_constructor {
             let struct_name_display = struct_name.display(self.env());
             let variant_name_display = variant
                 .map(|v| format!("::{}", v.display(self.symbol_pool())))
@@ -4589,7 +4605,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
             let in_order_fields = self.in_order_fields(&field_decls, fields);
             for (_, name, (exp_idx, field_exp)) in fields.iter() {
                 let (def_idx, field_name, translated_field_exp) =
-                    self.translate_exp_field(&field_decls, name, &instantiation, field_exp);
+                    self.translate_exp_field(&field_decls, name, &struct_inst_id.inst, field_exp);
                 if in_order_fields.contains(&def_idx) {
                     args.insert(def_idx, translated_field_exp);
                 } else {
@@ -4614,9 +4630,6 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                 ),
             )
         }
-        let struct_id = struct_entry
-            .module_id
-            .qualified_inst(struct_entry.struct_id, instantiation);
         let bindings = bindings
             .into_iter()
             .sorted_by_key(|(i, _)| *i)
@@ -4628,8 +4641,8 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
             .map(|(_, value)| value)
             .collect_vec();
 
-        let struct_ty = struct_id.to_type();
-        let struct_ty = self.check_type(loc, &struct_ty, expected_type, context);
+        let struct_ty = struct_inst_id.to_type();
+        let struct_ty = self.check_type(loc, &struct_ty, &expected_type, context);
         let mut field_args = args.into_iter().map(|e| e.into_exp()).collect_vec();
         if variant.is_none() && field_args.is_empty() {
             // The move compiler inserts a dummy field with the value of false
@@ -4642,10 +4655,10 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
             field_args.push(ExpData::Value(id, Value::Bool(false)).into_exp());
         }
         let id = self.new_node_id_with_type_loc(&struct_ty, loc);
-        self.set_node_instantiation(id, struct_id.inst);
+        self.set_node_instantiation(id, struct_inst_id.inst);
         let body = ExpData::Call(
             id,
-            Operation::Pack(struct_id.module_id, struct_id.id, variant),
+            Operation::Pack(struct_inst_id.module_id, struct_inst_id.id, variant),
             field_args,
         );
         // Fold the bindings and the body into result exp
@@ -4730,7 +4743,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                     self.error(
                         struct_name_loc,
                         &format!(
-                            "struct `{}` has no variant named `{}`",
+                            "enum `{}` has no variant named `{}`",
                             struct_name.display(self.env()),
                             name.display(self.symbol_pool())
                         ),
@@ -4738,8 +4751,34 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                     None
                 }
             },
-            _ => {
-                self.bug(struct_name_loc, "inconsistent struct definition");
+            (StructLayout::Singleton(..), Some(_)) => {
+                self.error(
+                    struct_name_loc,
+                    &format!(
+                        "struct `{}` does not have variants",
+                        struct_name.display(self.env())
+                    ),
+                );
+                None
+            },
+            (StructLayout::Variants(..), None) => {
+                self.error(
+                    struct_name_loc,
+                    &format!(
+                        "enum `{}` must be used with one of its variants",
+                        struct_name.display(self.env())
+                    ),
+                );
+                None
+            },
+            (StructLayout::None, _) => {
+                self.error(
+                    struct_name_loc,
+                    &format!(
+                        "native struct `{}` has no fields",
+                        struct_name.display(self.env())
+                    ),
+                );
                 None
             },
         }


Test patch:
diff --git a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied.exp b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied.exp
index f849cbe9d7022..4dc6e79fbcfaa 100644
--- a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied.exp
+++ b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied.exp
@@ -11,17 +11,6 @@ error: type `Coin` is missing required ability `drop` (type was inferred)
   │
   = required by instantiating type parameter `T:drop` of struct `S`
 
-error: type `R<key + integer>` is missing required ability `key` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied.move:12:30
-   │
- 3 │     struct R<T: key>  { r: T }
-   │              - declaration of type parameter `T`
-   ·
-12 │         R {r: R { r: _ } } = R { r: R { r: 0 }};
-   │                              ^
-   │
-   = required by instantiating type parameter `T:key` of struct `R`
-
 error: type `R<key>` is missing required ability `key` (type was inferred)
    ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied.move:12:30
    │
@@ -43,3 +32,4 @@ error: type `Coin` is missing required ability `drop` (type was inferred)
    │                ^
    │
    = required by instantiating type parameter `T:drop` of struct `S`
+   = required by instantiating type parameter `T:drop` of struct `S`
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_refutable_err.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_refutable_err.exp
new file mode 100644
index 0000000000000..2072c014526ea
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_refutable_err.exp
@@ -0,0 +1,29 @@
+// -- Model dump before bytecode pipeline
+module 0x815::m {
+    enum E {
+        None,
+        Some {
+            0: u64,
+        }
+    }
+    private fun t(self: m::E): u64 {
+        {
+          let m::E::Some{ 0: x } = self;
+          x
+        }
+    }
+} // end 0x815::m
+
+============ initial bytecode ================
+
+[variant baseline]
+fun m::t($t0: m::E): u64 {
+     var $t1: u64
+     var $t2: u64
+  0: $t2 := unpack_variant m::E::Some($t0)
+  1: $t1 := infer($t2)
+  2: return $t1
+}
+
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_refutable_err.move b/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_refutable_err.move
new file mode 100644
index 0000000000000..6a10a81d4bd45
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_refutable_err.move
@@ -0,0 +1,13 @@
+module 0x815::m {
+
+    enum E {
+        None,
+        Some(u64)
+    }
+
+    fun t(self: E): u64 {
+        // We currently allow matching refutable patterns with let
+        let Some(x) = self;
+        x
+    }
+}
diff --git a/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.exp b/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.exp
new file mode 100644
index 0000000000000..db8a5d33f66f5
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.exp
@@ -0,0 +1,26 @@
+// -- Model dump before bytecode pipeline
+module 0x1::m {
+    struct R {
+        value: bool,
+    }
+    private fun f1()
+        acquires m::R(*)
+     {
+        {
+          let x: &mut m::R = BorrowGlobal(Mutable)<m::R>(0x1);
+          select m::R.value<&mut m::R>(x) = false;
+          if Eq<bool>(select m::R.value<m::R>(BorrowGlobal(Immutable)<m::R>(0x1)), false) {
+            Tuple()
+          } else {
+            Abort(1)
+          };
+          select m::R.value<m::R>(BorrowGlobal(Mutable)<m::R>(0x1)) = true;
+          if Eq<bool>(select m::R.value<m::R>(BorrowGlobal(Immutable)<m::R>(0x1)), true) {
+            Tuple()
+          } else {
+            Abort(2)
+          };
+          Tuple()
+        }
+    }
+} // end 0x1::m
diff --git a/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.move b/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.move
new file mode 100644
index 0000000000000..91d71654bcffa
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.move
@@ -0,0 +1,12 @@
+module 0x1::m {
+
+  struct R has key, drop { value: bool }
+
+  fun f1() acquires R {
+    let x = &mut R[@0x1];
+    x.value = false;
+    assert!(R[@0x1].value == false, 1);
+    R[@0x1].value = true;
+    assert!(R[@0x1].value == true, 2);
+  }
+}
diff --git a/third_party/move/move-compiler-v2/tests/checking/specs/structs_err.exp b/third_party/move/move-compiler-v2/tests/checking/specs/structs_err.exp
index 77e76d82d18f6..e5233c7c08192 100644
--- a/third_party/move/move-compiler-v2/tests/checking/specs/structs_err.exp
+++ b/third_party/move/move-compiler-v2/tests/checking/specs/structs_err.exp
@@ -24,11 +24,11 @@ error: missing fields `y`, `z`
 40 │       S{x: x}
    │       ^^^^^^^
 
-error: cannot return `G<u64>` from a function with result type `G<bool>`
-   ┌─ tests/checking/specs/structs_err.move:45:7
+error: expected `bool` but found a value of type `u64`
+   ┌─ tests/checking/specs/structs_err.move:45:12
    │
 45 │       G{x: x, y: y}
-   │       ^^^^^^^^^^^^^
+   │            ^
 
 error: expected 1 type argument but 2 were provided
    ┌─ tests/checking/specs/structs_err.move:50:7
diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/bad_type_argument_arity_struct_unpack.exp b/third_party/move/move-compiler-v2/tests/checking/typing/bad_type_argument_arity_struct_unpack.exp
index 9593d670e3841..dbc956ddccf4d 100644
--- a/third_party/move/move-compiler-v2/tests/checking/typing/bad_type_argument_arity_struct_unpack.exp
+++ b/third_party/move/move-compiler-v2/tests/checking/typing/bad_type_argument_arity_struct_unpack.exp
@@ -4,7 +4,7 @@ error: expected 1 type argument but 0 were provided
   ┌─ tests/checking/typing/bad_type_argument_arity_struct_unpack.move:7:13
   │
 7 │         let S<> { f } = copy s;
-  │             ^^^^^^^^^
+  │             ^
 
 error: undeclared `f`
   ┌─ tests/checking/typing/bad_type_argument_arity_struct_unpack.move:8:9
@@ -16,7 +16,7 @@ error: expected 1 type argument but 2 were provided
   ┌─ tests/checking/typing/bad_type_argument_arity_struct_unpack.move:9:13
   │
 9 │         let S<u64, u64> { f } = copy s;
-  │             ^^^^^^^^^^^^^^^^^
+  │             ^
 
 error: undeclared `f`
    ┌─ tests/checking/typing/bad_type_argument_arity_struct_unpack.move:10:9
diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/native_structs_pack_unpack.exp b/third_party/move/move-compiler-v2/tests/checking/typing/native_structs_pack_unpack.exp
index a612877c96823..7c99c743777b4 100644
--- a/third_party/move/move-compiler-v2/tests/checking/typing/native_structs_pack_unpack.exp
+++ b/third_party/move/move-compiler-v2/tests/checking/typing/native_structs_pack_unpack.exp
@@ -1,23 +1,17 @@
 
 Diagnostics:
-bug: inconsistent struct definition
+error: native struct `C::T` has no fields
   ┌─ tests/checking/typing/native_structs_pack_unpack.move:9:9
   │
 9 │         C::T {}
   │         ^^^^
 
-bug: inconsistent struct definition
+error: native struct `C::T` has no fields
    ┌─ tests/checking/typing/native_structs_pack_unpack.move:12:13
    │
 12 │         let C::T {} = c;
    │             ^^^^
 
-error: unable to infer instantiation of type `_` (consider providing type arguments or annotating the type)
-   ┌─ tests/checking/typing/native_structs_pack_unpack.move:12:13
-   │
-12 │         let C::T {} = c;
-   │             ^^^^^^^
-
 error: field `f` not declared in struct `C::T`
    ┌─ tests/checking/typing/native_structs_pack_unpack.move:15:17
    │
diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/pack_invalid_argument.exp b/third_party/move/move-compiler-v2/tests/checking/typing/pack_invalid_argument.exp
index 5267da642aef9..ef5fdafec2d01 100644
--- a/third_party/move/move-compiler-v2/tests/checking/typing/pack_invalid_argument.exp
+++ b/third_party/move/move-compiler-v2/tests/checking/typing/pack_invalid_argument.exp
@@ -18,14 +18,14 @@ error: expected `u64` but found a value of type `bool`
 18 │             f: false,
    │                ^^^^^
 
-error: expected `Nat<u64>` but found a value of type `Nat<bool>`
-   ┌─ tests/checking/typing/pack_invalid_argument.move:19:17
+error: expected `u64` but found a value of type `bool`
+   ┌─ tests/checking/typing/pack_invalid_argument.move:19:26
    │
 19 │             n1: Nat { f: false },
-   │                 ^^^^^^^^^^^^^^^^
+   │                          ^^^^^
 
-error: expected `Nat<S>` but found a value of type `Nat<R>`
-   ┌─ tests/checking/typing/pack_invalid_argument.move:20:17
+error: expected `S` but found a value of type `R`
+   ┌─ tests/checking/typing/pack_invalid_argument.move:20:25
    │
 20 │             n2: Nat{ f: r }
-   │                 ^^^^^^^^^^^
+   │                         ^
diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/type_variable_join_single_pack_invalid.exp b/third_party/move/move-compiler-v2/tests/checking/typing/type_variable_join_single_pack_invalid.exp
index 17f26441bdf07..fd8c0f6807f71 100644
--- a/third_party/move/move-compiler-v2/tests/checking/typing/type_variable_join_single_pack_invalid.exp
+++ b/third_party/move/move-compiler-v2/tests/checking/typing/type_variable_join_single_pack_invalid.exp
@@ -6,8 +6,14 @@ error: expected `bool` but found a value of type `integer`
 5 │         let b = Box { f1: false, f2: 1 };
   │                                      ^
 
-error: expected `Box<integer>` but found a value of type `Box<bool>`
-  ┌─ tests/checking/typing/type_variable_join_single_pack_invalid.move:6:55
+error: expected `integer` but found a value of type `bool`
+  ┌─ tests/checking/typing/type_variable_join_single_pack_invalid.move:6:65
   │
 6 │         let b2 = Box { f1: Box { f1: 0, f2: 0 }, f2:  Box { f1: false, f2: false } };
-  │                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  │                                                                 ^^^^^
+
+error: expected `integer` but found a value of type `bool`
+  ┌─ tests/checking/typing/type_variable_join_single_pack_invalid.move:6:76
+  │
+6 │         let b2 = Box { f1: Box { f1: 0, f2: 0 }, f2:  Box { f1: false, f2: false } };
+  │                                                                            ^^^^^
diff --git a/third_party/move/move-compiler-v2/tests/checking/variants/variants_check_err.exp b/third_party/move/move-compiler-v2/tests/checking/variants/variants_check_err.exp
index ce154d33e000c..57e291035e566 100644
--- a/third_party/move/move-compiler-v2/tests/checking/variants/variants_check_err.exp
+++ b/third_party/move/move-compiler-v2/tests/checking/variants/variants_check_err.exp
@@ -12,7 +12,7 @@ error: variants not allowed in this context
 27 │     fun missplaced_variant(self: Color::Red): bool {
    │                                  ^^^^^^^^^^
 
-error: undeclared struct `m::missplaced_variant`
+error: variants not allowed in this context
    ┌─ tests/checking/variants/variants_check_err.move:28:9
    │
 28 │         0x815::m::missplaced_variant::Red();
diff --git a/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.exp b/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.exp
new file mode 100644
index 0000000000000..1365da99cef53
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.exp
@@ -0,0 +1,47 @@
+// -- Model dump before bytecode pipeline
+module 0x815::m {
+    enum Color {
+        RGB {
+            red: u64,
+            green: u64,
+            blue: u64,
+        }
+        Red,
+        Blue,
+    }
+    private fun t0(): bool {
+        {
+          let c: m::Color = pack m::Color::Red();
+          Eq<u64>(select_variants m::Color.RGB.red<m::Color>(c), 1)
+        }
+    }
+    private fun t1(): bool {
+        {
+          let c: m::Color = pack m::Color::Red();
+          Eq<u64>(select_variants m::Color.RGB.red<m::Color>(c), 1)
+        }
+    }
+    private fun t2(): bool {
+        {
+          let c: m::Color = pack m::Color::Blue();
+          Eq<u64>(select_variants m::Color.RGB.red<m::Color>(c), 1)
+        }
+    }
+    private fun t3(): bool {
+        {
+          let c: m::Color = pack m::Color::Blue();
+          Eq<u64>(select_variants m::Color.RGB.red<m::Color>(c), 1)
+        }
+    }
+    private fun t4(c: &m::Color) {
+        match (c) {
+          m::Color::Red => {
+            Abort(1)
+          }
+          m::Color::Blue => {
+            Abort(2)
+          }
+        }
+
+    }
+} // end 0x815::m
diff --git a/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.move b/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.move
new file mode 100644
index 0000000000000..7d75059a3ee73
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.move
@@ -0,0 +1,31 @@
+module 0x815::m {
+
+    enum Color {
+        RGB{red: u64, green: u64, blue: u64},
+        Red,
+        Blue(),
+    }
+
+    fun t0(): bool {
+        let c = Color::Red; // no error expected
+        c.red == 1
+    }
+    fun t1(): bool {
+        let c : Color = Red; // no error expected
+        c.red == 1
+    }
+
+    fun t2(): bool {
+        let c : Color = Blue; // no error expected
+        c.red == 1
+    }
+
+    fun t3(): bool {
+        let c : Color = Blue(); // no error expected
+        c.red == 1
+    }
+
+    fun t4(c: &Color) {
+        match (c) { Red => abort 1, Blue => abort 2 } // no error
+    }
+}
diff --git a/third_party/move/move-compiler-v2/tests/checking/variants/variants_inference.exp b/third_party/move/move-compiler-v2/tests/checking/variants/variants_inference.exp
new file mode 100644
index 0000000000000..66bdf640475bd
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/checking/variants/variants_inference.exp
@@ -0,0 +1,19 @@
+
+Diagnostics:
+error: undeclared struct `m::Red`
+   ┌─ tests/checking/variants/variants_inference.move:17:14
+   │
+17 │         take(Red{}) // errors expected because of bottom-up type inference
+   │              ^^^
+
+error: undeclared struct `m::Red`
+   ┌─ tests/checking/variants/variants_inference.move:21:17
+   │
+21 │         let c = Red{}; // error expected
+   │                 ^^^
+
+error: enum `m::Color` must be used with one of its variants
+   ┌─ tests/checking/variants/variants_inference.move:26:17
+   │
+26 │         let c = Color{}; // error expected
+   │                 ^^^^^
diff --git a/third_party/move/move-compiler-v2/tests/checking/variants/variants_inference.move b/third_party/move/move-compiler-v2/tests/checking/variants/variants_inference.move
new file mode 100644
index 0000000000000..11fef7b829f42
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/checking/variants/variants_inference.move
@@ -0,0 +1,34 @@
+module 0x815::m {
+
+    enum Color {
+        RGB{red: u64, green: u64, blue: u64},
+        Red,
+        Blue(u64),
+    }
+
+    fun take(_c: Color){}
+
+    fun t1(): bool {
+        let c : Color = Red{}; // no error expected
+        c.red == 1
+    }
+
+    fun t2() {
+        take(Red{}) // errors expected because of bottom-up type inference
+    }
+
+    fun t3(): Color {
+        let c = Red{}; // error expected
+        c
+    }
+
+    fun t4(): Color {
+        let c = Color{}; // error expected
+        c
+    }
+
+    fun t5(): Color {
+        let c : Color = Blue(0); // no error expected
+        c
+    }
+}
diff --git a/third_party/move/move-model/tests/sources/structs_err.exp b/third_party/move/move-model/tests/sources/structs_err.exp
index 9829b115f35e8..44e351196c26a 100644
--- a/third_party/move/move-model/tests/sources/structs_err.exp
+++ b/third_party/move/move-model/tests/sources/structs_err.exp
@@ -22,11 +22,11 @@ error: missing fields `y`, `z`
 40 │       S{x: x}
    │       ^^^^^^^
 
-error: cannot return `G<u64>` from a function with result type `G<bool>`
-   ┌─ tests/sources/structs_err.move:45:7
+error: expected `bool` but found a value of type `u64`
+   ┌─ tests/sources/structs_err.move:45:12
    │
 45 │       G{x: x, y: y}
-   │       ^^^^^^^^^^^^^
+   │            ^
 
 error: expected 1 type argument but 2 were provided
    ┌─ tests/sources/structs_err.move:50:7