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

Issue:
[Bug][move-compiler-v2] Compiler V2 misses key ability errors, with an edited test yields a "bug"
# 🐛 Bug

Compiler V1 catches some "missing ability `key`" errors which are missed by Compiler V2:
- third_party/move/move-compiler/tests/move_check/typing/pack_constraint_not_satisfied.exp
- third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied.exp

Reducing the test to try to eliminate error shadowing:
- third_party/move/move-compiler/tests/move_check/typing/pack_constraint_not_satisfied2.exp
- third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied2.exp
yields a compiler "bug" from V2 instead of the expected "key" errors:
```
Diagnostics:
bug: unexpected type: _
  ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied2.move:6:9
  │
6 │     fun t0() {
  │         ^^
```






Discussion / hints:
Might be fixed by now as something in this area was recently fixed.

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 992dbb07cf603..8f16fa60111de 100644
--- a/third_party/move/move-model/src/builder/exp_builder.rs
+++ b/third_party/move/move-model/src/builder/exp_builder.rs
@@ -2089,7 +2089,8 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
                 Pattern::Struct(sid, std, variant, patterns) => {
                     let mut new_inst = vec![];
                     for ty in &std.inst {
-                        let nty = subs.specialize(ty);
+                        // use `specialize_with_defaults` to get type info from constraints
+                        let nty: Type = subs.specialize_with_defaults(ty);
                         new_inst.push(nty);
                     }
                     let mut new_std = std.clone();
diff --git a/third_party/move/move-model/src/ty.rs b/third_party/move/move-model/src/ty.rs
index 2bb9288cbd53b..6e947c60d7442 100644
--- a/third_party/move/move-model/src/ty.rs
+++ b/third_party/move/move-model/src/ty.rs
@@ -453,12 +453,14 @@ impl Constraint {
 
     /// Joins the two constraints. If they are incompatible, produces a type unification error.
     /// Otherwise, returns true if `self` absorbs the `other` constraint (and waives the `other`).
+    /// ctx_opt is for additional error info
     pub fn join(
         &mut self,
         context: &mut impl UnificationContext,
         subs: &mut Substitution,
         loc: &Loc,
         other: &Constraint,
+        ctx_opt: Option<ConstraintContext>,
     ) -> Result<bool, TypeUnificationError> {
         match (&mut *self, other) {
             (Constraint::SomeNumber(opts1), Constraint::SomeNumber(opts2)) => {
@@ -543,6 +545,34 @@ impl Constraint {
                 *a1 = a1.union(*a2);
                 Ok(true)
             },
+            // After the above checks on same type of constraint
+            // Check compatibility between ability and number
+            // This check is needed because sometime the concrete integer type is not available
+            // TODO: check other combination of constraints may be necessary as well.
+            (Constraint::HasAbilities(a1, _), Constraint::SomeNumber(_)) => {
+                let unsupported_abilities = a1.setminus(AbilitySet::PRIMITIVES);
+                if !unsupported_abilities.is_empty() {
+                    return Err(TypeUnificationError::MissingAbilitiesForConstraints(
+                        loc.clone(),
+                        other.clone(),
+                        unsupported_abilities,
+                        ctx_opt,
+                    ));
+                }
+                Ok(false)
+            },
+            (Constraint::SomeNumber(_), Constraint::HasAbilities(a1, _)) => {
+                let unsupported_abilities = a1.setminus(AbilitySet::PRIMITIVES);
+                if !unsupported_abilities.is_empty() {
+                    return Err(TypeUnificationError::MissingAbilitiesForConstraints(
+                        loc.clone(),
+                        self.clone(),
+                        unsupported_abilities,
+                        ctx_opt,
+                    ));
+                }
+                Ok(false)
+            },
             // After the above checks, if one of the constraints is
             // accumulating, indicate its compatible but cannot be joined.
             (c1, c2) if c1.accumulating() || c2.accumulating() => Ok(false),
@@ -702,6 +732,8 @@ pub enum TypeUnificationError {
     ),
     /// The `HasAbilities` constraint failed: `MissingAbilities(loc, ty, missing, ctx)`.
     MissingAbilities(Loc, Type, AbilitySet, Option<ConstraintContext>),
+    /// The `HasAbilities` constraint failed: `MissingAbilitiesForConstraints(loc, ctr, missing, ctx)`.
+    MissingAbilitiesForConstraints(Loc, Constraint, AbilitySet, Option<ConstraintContext>),
     /// The two constraints are incompatible and cannot be joined.
     ConstraintsIncompatible(Loc, Constraint, Constraint),
     /// A cyclic substitution when trying to unify the given types.
@@ -1668,7 +1700,7 @@ impl Substitution {
         let mut absorbed = false;
         for (_, _, c) in current.iter_mut() {
             // Join constraints. If join returns true the constraint is absorbed.
-            absorbed = c.join(context, self, &loc, &ctr)?;
+            absorbed = c.join(context, self, &loc, &ctr, ctx_opt.clone())?;
             if absorbed {
                 break;
             }
@@ -3015,6 +3047,27 @@ impl TypeUnificationError {
                     labels,
                 )
             },
+            TypeUnificationError::MissingAbilitiesForConstraints(_, ctr, missing, ctx_opt) => {
+                let (note, hints, labels) = ctx_opt
+                    .as_ref()
+                    .map(|ctx| ctx.describe(display_context))
+                    .unwrap_or_default();
+                (
+                    format!(
+                        "constraint `{}` does not have required {} `{}`{}",
+                        ctr.display(display_context),
+                        pluralize("ability", missing.iter().count()),
+                        missing,
+                        if !note.is_empty() {
+                            format!(" ({})", note)
+                        } else {
+                            "".to_string()
+                        }
+                    ),
+                    hints,
+                    labels,
+                )
+            },
             TypeUnificationError::ConstraintUnsatisfied(_, ty, order, constr, ctx_opt) => {
                 let item_name = || match ctx_opt {
                     Some(ConstraintContext {


Test patch:
diff --git a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.exp b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.exp
index f6d70f0d9eaaf..bcc27300553be 100644
--- a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.exp
+++ b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.exp
@@ -1,213 +1,219 @@
 
 Diagnostics:
 error: type `S` is missing required ability `key` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:28:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:32:9
    │
 15 │     fun both<R: key, C: copy>(_r: R, _c: C) {
    │              - declaration of type parameter `R`
    ·
-28 │         both(S{}, Coin{});
+32 │         both(S{}, Coin{});
    │         ^^^^
    │
    = required by instantiating type parameter `R:key` of function `both`
 
-error: type `Coin` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:29:9
-   │
-15 │     fun both<R: key, C: copy>(_r: R, _c: C) {
-   │                      - declaration of type parameter `C`
-   ·
-29 │         both(0, Coin{})
-   │         ^^^^
+error: constraint `integer` does not have required ability `key`
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:33:14
    │
-   = required by instantiating type parameter `C:copy` of function `both`
+33 │         both(0, Coin{})
+   │              ^
 
 error: type `Box<C>` is missing required ability `key` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:33:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:37:9
    │
 15 │     fun both<R: key, C: copy>(_r: R, _c: C) {
    │              - declaration of type parameter `R`
    ·
-33 │         both(new_box<C>(), new_box<R>())
+37 │         both(new_box<C>(), new_box<R>())
    │         ^^^^
    │
    = required by instantiating type parameter `R:key` of function `both`
 
 error: type `Box3<C, C, C>` is missing required ability `key` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:37:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:41:9
    │
-23 │     fun rsrc<R: key>(_r: R) {
+27 │     fun rsrc<R: key>(_r: R) {
    │              - declaration of type parameter `R`
    ·
-37 │         rsrc(new_box3<C, C, C>());
+41 │         rsrc(new_box3<C, C, C>());
    │         ^^^^
    │
    = required by instantiating type parameter `R:key` of function `rsrc`
 
 error: type `R` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:39:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:43:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-39 │         cpy(new_box3<R, C, C>());
+43 │         cpy(new_box3<R, C, C>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `C` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:40:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:44:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-40 │         cpy(new_box3<C, R, C>());
+44 │         cpy(new_box3<C, R, C>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `C` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:41:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:45:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-41 │         cpy(new_box3<C, C, R>());
+45 │         cpy(new_box3<C, C, R>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `C` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:43:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:47:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-43 │         cpy(new_box3<C, R, R>());
+47 │         cpy(new_box3<C, R, R>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `R` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:44:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:48:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-44 │         cpy(new_box3<R, C, R>());
+48 │         cpy(new_box3<R, C, R>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `R` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:45:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:49:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-45 │         cpy(new_box3<R, R, C>());
+49 │         cpy(new_box3<R, R, C>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `R` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:47:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:51:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-47 │         cpy(new_box3<R, R, R>());
+51 │         cpy(new_box3<R, R, R>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `U` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:51:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:55:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-51 │         cpy(new_box3<U, C, C>());
+55 │         cpy(new_box3<U, C, C>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `C` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:52:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:56:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-52 │         cpy(new_box3<C, U, C>());
+56 │         cpy(new_box3<C, U, C>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `C` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:53:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:57:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-53 │         cpy(new_box3<C, C, U>());
+57 │         cpy(new_box3<C, C, U>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `C` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:55:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:59:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-55 │         cpy(new_box3<C, U, U>());
+59 │         cpy(new_box3<C, U, U>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `U` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:56:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:60:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-56 │         cpy(new_box3<U, C, U>());
+60 │         cpy(new_box3<U, C, U>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `U` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:57:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:61:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-57 │         cpy(new_box3<U, U, C>());
+61 │         cpy(new_box3<U, U, C>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
 
 error: type `U` is missing required ability `copy` (type was inferred)
-   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:59:9
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:63:9
    │
-19 │     fun cpy<C: copy>(_c: C) {
+23 │     fun cpy<C: copy>(_c: C) {
    │             - declaration of type parameter `C`
    ·
-59 │         cpy(new_box3<U, U, U>());
+63 │         cpy(new_box3<U, U, U>());
    │         ^^^
    │
    = required by instantiating type parameter `T1` of struct `Box3`
    = required by instantiating type parameter `C:copy` of function `cpy`
+
+error: type `Coin` is missing required ability `copy` (type was inferred)
+   ┌─ tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move:67:9
+   │
+19 │     fun both_1<R: copy, C: key>(_r: R, _c: C) {
+   │                - declaration of type parameter `R`
+   ·
+67 │         both_1(Coin{}, 0)
+   │         ^^^^^^
+   │
+   = required by instantiating type parameter `R:copy` of function `both_1`
diff --git a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move
index 22470e4d2fbb4..7120211abf571 100644
--- a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move
+++ b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/module_call_constraints_not_satisfied.move
@@ -16,6 +16,10 @@ module 0x8675309::M {
         abort 0
     }
 
+    fun both_1<R: copy, C: key>(_r: R, _c: C) {
+        abort 0
+    }
+
     fun cpy<C: copy>(_c: C) {
         abort 0
     }
@@ -58,4 +62,8 @@ module 0x8675309::M {
 
         cpy(new_box3<U, U, U>());
     }
+
+    fun t4<R: key, C: drop>() {
+        both_1(Coin{}, 0)
+    }
 }
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 4dc6e79fbcfaa..735d68e511b94 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
@@ -1,5 +1,11 @@
 
 Diagnostics:
+error: constraint `integer` does not have required ability `key`
+  ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied.move:7:27
+  │
+7 │         R {r:_ } = R { r: 0 };
+  │                           ^
+
 error: type `Coin` is missing required ability `drop` (type was inferred)
   ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied.move:8:9
   │
@@ -11,6 +17,17 @@ error: type `Coin` is missing required ability `drop` (type was inferred)
   │
   = required by instantiating type parameter `T:drop` of struct `S`
 
+error: type `R<*error*>` 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
    │
@@ -22,6 +39,12 @@ error: type `R<key>` is missing required ability `key` (type was inferred)
    │
    = required by instantiating type parameter `T:key` of struct `R`
 
+error: constraint `integer` does not have required ability `key`
+   ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied.move:12:44
+   │
+12 │         R {r: R { r: _ } } = R { r: R { r: 0 }};
+   │                                            ^
+
 error: type `Coin` is missing required ability `drop` (type was inferred)
    ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied.move:13:16
    │
diff --git a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied2.exp b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied2.exp
index 159105ab0cca0..0310e87e78cca 100644
--- a/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied2.exp
+++ b/third_party/move/move-compiler-v2/tests/ability-check/v1-typing/pack_constraint_not_satisfied2.exp
@@ -1,7 +1,7 @@
 
 Diagnostics:
-bug: unexpected type: _
-  ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied2.move:6:9
+error: constraint `integer` does not have required ability `key`
+  ┌─ tests/ability-check/v1-typing/pack_constraint_not_satisfied2.move:7:27
   │
-6 │     fun t0() {
-  │         ^^
+7 │         R {r:_ } = R { r: 0 };
+  │                           ^
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629.exp
new file mode 100644
index 0000000000000..74202644fcec6
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629.exp
@@ -0,0 +1,77 @@
+// -- Model dump before bytecode pipeline
+module 0x8675309::M {
+    struct R<T> {
+        r: T,
+    }
+    struct X<T> {
+        r: T,
+    }
+    private fun t0() {
+        {
+          let y: R<X<u64>> = pack M::R<X<u64>>(pack M::X<u64>(0));
+          {
+            let M::R<X<u64>>{ r: _r } = y;
+            Tuple()
+          }
+        }
+    }
+    private fun t0_u128() {
+        {
+          let y: R<X<u128>> = pack M::R<X<u128>>(pack M::X<u128>(18446744073709551616));
+          {
+            let M::R<X<u128>>{ r: _r } = y;
+            Tuple()
+          }
+        }
+    }
+} // end 0x8675309::M
+
+// -- Sourcified model before bytecode pipeline
+module 0x8675309::M {
+    struct R<T: key> {
+        r: T,
+    }
+    struct X<T> has drop, key {
+        r: T,
+    }
+    fun t0() {
+        let y = R<X<u64>>{r: X<u64>{r: 0}};
+        let R<X<u64>>{r: _r} = y;
+    }
+    fun t0_u128() {
+        let y = R<X<u128>>{r: X<u128>{r: 18446744073709551616u128}};
+        let R<X<u128>>{r: _r} = y;
+    }
+}
+
+============ initial bytecode ================
+
+[variant baseline]
+fun M::t0() {
+     var $t0: 0x8675309::M::R<0x8675309::M::X<u64>>
+     var $t1: 0x8675309::M::X<u64>
+     var $t2: u64
+     var $t3: 0x8675309::M::X<u64>
+  0: $t2 := 0
+  1: $t1 := pack 0x8675309::M::X<u64>($t2)
+  2: $t0 := pack 0x8675309::M::R<0x8675309::M::X<u64>>($t1)
+  3: $t3 := unpack 0x8675309::M::R<0x8675309::M::X<u64>>($t0)
+  4: return ()
+}
+
+
+[variant baseline]
+fun M::t0_u128() {
+     var $t0: 0x8675309::M::R<0x8675309::M::X<u128>>
+     var $t1: 0x8675309::M::X<u128>
+     var $t2: u128
+     var $t3: 0x8675309::M::X<u128>
+  0: $t2 := 18446744073709551616
+  1: $t1 := pack 0x8675309::M::X<u128>($t2)
+  2: $t0 := pack 0x8675309::M::R<0x8675309::M::X<u128>>($t1)
+  3: $t3 := unpack 0x8675309::M::R<0x8675309::M::X<u128>>($t0)
+  4: return ()
+}
+
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629.move b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629.move
new file mode 100644
index 0000000000000..4d521eb2741ec
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629.move
@@ -0,0 +1,17 @@
+module 0x8675309::M {
+    const MAX_U64: u128 = 18446744073709551615;
+    struct R<T: key>  { r: T }
+    struct X<T> has key, drop {
+        r: T
+    }
+
+    fun t0() {
+        let y = R { r: X { r: 0} };
+        let R { r: _r } = y;
+    }
+
+    fun t0_u128() {
+        let y = R { r: X { r: MAX_U64 + 1} };
+        let R { r: _r } = y;
+    }
+}
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629_fail.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629_fail.exp
new file mode 100644
index 0000000000000..2a80c7c6433bd
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629_fail.exp
@@ -0,0 +1,7 @@
+
+Diagnostics:
+error: constraint `integer` does not have required ability `key`
+  ┌─ tests/bytecode-generator/bug_14629_fail.move:6:32
+  │
+6 │         let R { r: _ } = R {r: 0};
+  │                                ^
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629_fail.move b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629_fail.move
new file mode 100644
index 0000000000000..839d55f56bf5e
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14629_fail.move
@@ -0,0 +1,8 @@
+module 0x8675309::M {
+    // struct Coin {}
+    struct R<T: key>  { r: T }
+
+    fun t1() {
+        let R { r: _ } = R {r: 0};
+    }
+}
diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/global_builtins_invalid.exp b/third_party/move/move-compiler-v2/tests/checking/typing/global_builtins_invalid.exp
index 73c27aa098912..8297687e57d65 100644
--- a/third_party/move/move-compiler-v2/tests/checking/typing/global_builtins_invalid.exp
+++ b/third_party/move/move-compiler-v2/tests/checking/typing/global_builtins_invalid.exp
@@ -42,6 +42,12 @@ error: cannot use `integer` with an operator which expects a value of type `R`
 14 │         let () = move_to<R>(a, 0);
    │                                ^
 
+error: constraint `integer` does not have required ability `key`
+   ┌─ tests/checking/typing/global_builtins_invalid.move:15:29
+   │
+15 │         let () = move_to(a, 0);
+   │                             ^
+
 error: cannot use `integer` with an operator which expects a value of type `address`
    ┌─ tests/checking/typing/global_builtins_invalid.move:16:39
    │