       let conType = self.getASTTypeOfConstructor(constructor)
       switch conType {
       case .pi(_, _):
-        let (payloadLowering, loweredTy)
-          = self.lowerDataConstructorPayloadType(constructor.key, conType)
+        let payloadLowering = Lowering(name: constructor.key)
+        self.completeDataConstructorPayloadType(constructor.key, conType,
+                                                environment, payloadLowering)
         hasOnlyTrivialCons = hasOnlyTrivialCons && payloadLowering.trivial
-        forceAddressOnly = forceAddressOnly || payloadLowering.addressOnly
-        loweredConstructors.append((conName, loweredTy))
+        loweredConstructors.append((conName, payloadLowering.type))
       default:
         loweredConstructors.append((conName, nil))
       }
     }
-    if forceAddressOnly {
-      let loweredType = self.module!.dataType(name: name,
+
+    let loweredIndices: GIRType
+    switch origType {
+    case .pi(_, _):
+      let payloadLowering = Lowering(name: QualifiedName())
+      self.completeDataConstructorPayloadType(name, origType,
+                                              .init([]), payloadLowering)
+      loweredIndices = payloadLowering.type
+    default:
+      loweredIndices = TypeType.shared
+    }
+
+    if hasOnlyTrivialCons {
+      let lowType = self.module!.dataType(name: name,
                                               module: self.module,
-                                              category: .address)
-      loweredType.addConstructors(loweredConstructors)
-      return AddressOnlyLowering(loweredType)
-    } else if hasOnlyTrivialCons {
-      let loweredType = self.module!.dataType(name: name,
+                                              indices: loweredIndices,
+                                              category: .object)
+      let lowerKey = CacheKey(loweredType: lowType)
+      lowType.addConstructors(loweredConstructors)
+      self.loweringCache[lowerKey] = lowering.completeTrivial(type: lowType)
+      return
+    } else {
+      let lowType = self.module!.dataType(name: name,
                                               module: self.module,
+                                              indices: loweredIndices,
                                               category: .object)
-      loweredType.addConstructors(loweredConstructors)
-      return TrivialLowering(loweredType)
-    } else {
-      let loweredType = self.module!.dataType(name: name,
-                                              module: self.module,
-                                              category: .object)
-      loweredType.addConstructors(loweredConstructors)
-      return NonTrivialLowering(loweredType)
-    }
-  }
-
-  fileprivate func lowerDataConstructorPayloadType(
+      let lowerKey = CacheKey(loweredType: lowType)
+      lowType.addConstructors(loweredConstructors)
+      self.loweringCache[lowerKey] = lowering.completeNonTrivial(type: lowType)
+      return
+    }
+  }
+
+  fileprivate func completeDataConstructorPayloadType(
     _ name: QualifiedName,
-    _ ty: Type<TT>
-  ) -> (Lowering, TupleType) {
+    _ ty: Type<TT>,
+    _ env: Environment,
+    _ completing: Lowering
+  ) {
     guard case .pi(_, _) = ty else {
       fatalError("Must lower an arrow type")
     }
     let (tel, _) = self.tc.unrollPi(ty)
     var hasOnlyTrivialElements = true
-    var forceAddressOnly = false
+    var forceBox = false
     var eltTypes = [GIRType]()
     eltTypes.reserveCapacity(tel.count)
-    for (_, type) in tel {
-      let eltLowering = self.lower(type)
-      hasOnlyTrivialElements = hasOnlyTrivialElements && eltLowering.trivial
-      forceAddressOnly = forceAddressOnly || eltLowering.addressOnly
+    for (idx, (_, type)) in tel.enumerated() {
+      // swiftlint:disable force_try
+      let substTy = try! type.applySubstitution(.strengthen(idx),
+                                                tc.eliminate(_:_:))
+      let eltLowering = self.lower(substTy, in: env)
+      hasOnlyTrivialElements = hasOnlyTrivialElements
+        && eltLowering.isComplete && eltLowering.trivial
+      forceBox = forceBox || !eltLowering.isComplete
+      guard eltLowering.isComplete else {
+        let seedType = self.module!.dataType(name: eltLowering.name,
+                                             module: self.module,
+                                             indices: nil,
+                                             category: .object)
+        eltTypes.append(seedType)
+        continue
+      }
       eltTypes.append(eltLowering.type)
     }
     let payloadKey = name.string
-    if forceAddressOnly {
-      let ty = TupleType(elements: eltTypes, category: .address)
-      let lowering = AddressOnlyLowering(ty)
+    if forceBox {
+      let ty = BoxType(TupleType(elements: eltTypes, category: .object))
+      let lowering = completing.completeNonTrivial(type: ty)
       self.loweringCache[CacheKey(loweredType: ty)] = lowering
       self.loweringCache[CacheKey(payloadOf: payloadKey)] = lowering
-      return (lowering, ty)
     } else if hasOnlyTrivialElements {
       let ty = TupleType(elements: eltTypes, category: .object)
-      let lowering = TrivialLowering(ty)
+      let lowering = completing.completeTrivial(type: ty)
       self.loweringCache[CacheKey(loweredType: ty)] = lowering
       self.loweringCache[CacheKey(payloadOf: payloadKey)] = lowering
-      return (lowering, ty)
     } else {
       let ty = TupleType(elements: eltTypes, category: .object)
-      let lowering = NonTrivialLowering(ty)
+      let lowering = completing.completeNonTrivial(type: ty)
       self.loweringCache[CacheKey(loweredType: ty)] = lowering
       self.loweringCache[CacheKey(payloadOf: payloadKey)] = lowering
-      return (lowering, ty)
-    }
-  }
-
-  private func dataTypeAsTrivialNat(
+    }
+  }
+
+  private func completeDataTypeAsTrivialNat(
     _ name: QualifiedName, _ origType: Type<TT>,
-    _ constructors: [Opened<QualifiedName, TT>]
-    ) -> Lowering? {
+    _ constructors: [Opened<QualifiedName, TT>],
+    _ completing: Lowering
+    ) -> Bool {
     guard constructors.count == 2 else {
-      return nil
+      return false
     }
 
     let oneCon = constructors[0]

  }

  private func withCachedKey(
    _ key: QualifiedName, _ f: (QualifiedName) -> Lowering) -> Lowering {
    guard !self.inProgressLowerings.contains(key) else {
      return RecursiveLowering(key)
    }

    self.inProgressLowerings.insert(key)
    let result = f(key)
    self.inProgressLowerings.remove(key)
    return result
  }
}

private class RecursiveLowering: TypeConverter.Lowering {
  init(_ name: QualifiedName) {
    super.init(type: BoxType(name),
               isTrivial: false, isAddressOnly: false)
  }
}

private class TrivialLowering: TypeConverter.Lowering {
  init(_ type: GIRType) {
    super.init(type: type, isTrivial: true, isAddressOnly: false)
  }
}

private class NonTrivialLowering: TypeConverter.Lowering {
  init(_ type: GIRType) {
    super.init(type: type, isTrivial: false, isAddressOnly: false)
  }
}

private class TrivialAddressOnlyLowering: TypeConverter.Lowering {
  init(_ type: GIRType) {
    super.init(type: type, isTrivial: true, isAddressOnly: true)
  }
}

private class AddressOnlyLowering: TypeConverter.Lowering {
  init(_ type: GIRType) {
    super.init(type: type, isTrivial: false, isAddressOnly: true)
  }
}

    _ con: Opened<QualifiedName, TT>
  ) -> GIRType {
    let cacheKey = CacheKey(payloadOf: con.key.string)
    let conType = self.getASTTypeOfConstructor(con)
    guard case .pi(_, _) = conType else {
      return TupleType(elements: [], category: .object)
    }
    return self.withCachedKey(cacheKey, { _, lowering in
      self.completeDataConstructorPayloadType(con.key, conType,
                                              .init([]), lowering)
    }).type
  }

  private func getTypeKey(_ type: Type<TT>) -> CacheKey {
