Reland "Towards synthesizing Kotlin @Metadata: type projection (type arguments)."

This reverts commit b93b4e950d338f85a7ffc5c7e59dd0c4b9ead244.

Change-Id: Idce97aa98941316d543d619b3cfff94a77f3554a
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 47763c5..743f406 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -269,6 +269,26 @@
       outer.innerTypeSignature = inner;
       inner.enclosingTypeSignature = outer;
     }
+
+    // TODO(b/129925954): rewrite GenericSignatureRewriter with this pattern?
+    public interface Converter<R> {
+      R init();
+      R visitType(DexType type, R result);
+      R visitTypeArgument(FieldTypeSignature typeArgument, R result);
+      R visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, R result);
+    }
+
+    public <R> R convert(Converter<R> converter) {
+      R result = converter.init();
+      result = converter.visitType(type, result);
+      for (FieldTypeSignature typeArgument : typeArguments) {
+        result = converter.visitTypeArgument(typeArgument, result);
+      }
+      if (innerTypeSignature != null) {
+        result = converter.visitInnerTypeSignature(innerTypeSignature, result);
+      }
+      return result;
+    }
   }
 
   public static class ArrayTypeSignature extends FieldTypeSignature {
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 941d7e2..a1cdd66 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -83,7 +83,21 @@
             .put(factory.boxedNumberType, factory.createType(addKotlinPrefix("Number;")))
             .put(factory.comparableType, factory.createType(addKotlinPrefix("Comparable;")))
             .put(factory.enumType, factory.createType(addKotlinPrefix("Enum;")))
-            // TODO(b/70169921): (Mutable) Collections?
+            // Collections
+            .put(factory.iteratorType, factory.createType(addKotlinPrefix("collections/Iterator;")))
+            .put(
+                factory.collectionType,
+                factory.createType(addKotlinPrefix("collections/Collection;")))
+            .put(factory.listType, factory.createType(addKotlinPrefix("collections/List;")))
+            .put(factory.setType, factory.createType(addKotlinPrefix("collections/Set;")))
+            .put(factory.mapType, factory.createType(addKotlinPrefix("collections/Map;")))
+            .put(
+                factory.listIteratorType,
+                factory.createType(addKotlinPrefix("collections/ListIterator;")))
+            .put(factory.iterableType, factory.createType(addKotlinPrefix("collections/Iterable;")))
+            .put(
+                factory.mapEntryType,
+                factory.createType(addKotlinPrefix("collections/Map$Entry;")))
             // .../jvm/functions/FunctionN -> .../FunctionN
             .putAll(
                 IntStream.rangeClosed(0, 22).boxed().collect(Collectors.toMap(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 6085350..4a65b7c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -61,14 +61,16 @@
     List<KmType> superTypes = kmClass.getSupertypes();
     superTypes.clear();
     for (DexType itfType : clazz.interfaces.values) {
-      KmType kmType = toRenamedKmType(itfType, appView, lens);
+      // TODO(b/129925954): Use GenericSignature.ClassSignature#superInterfaceSignatures
+      KmType kmType = toRenamedKmType(itfType, null, appView, lens);
       if (kmType != null) {
         superTypes.add(kmType);
       }
     }
     assert clazz.superType != null;
     if (clazz.superType != appView.dexItemFactory().objectType) {
-      KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, appView, lens);
+      // TODO(b/129925954): Use GenericSignature.ClassSignature#superClassSignature
+      KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, null, appView, lens);
       if (kmTypeForSupertype != null) {
         superTypes.add(kmTypeForSupertype);
       }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index b16d90a..81324e5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -19,14 +19,21 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.List;
 import kotlinx.metadata.KmConstructor;
 import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmProperty;
 import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeProjection;
 import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.KmVariance;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
 
 class KotlinMetadataSynthesizer {
@@ -88,8 +95,20 @@
     return descriptorToKotlinClassifier(renamedType.toDescriptorString());
   }
 
+  // TODO(b/148654451): Canonicalization?
   static KmType toRenamedKmType(
-      DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+      DexType type,
+      TypeSignature typeSignature,
+      AppView<AppInfoWithLiveness> appView,
+      NamingLens lens) {
+    if (typeSignature != null
+        && typeSignature.isFieldTypeSignature()
+        && typeSignature.asFieldTypeSignature().isClassTypeSignature()
+        && typeSignature.asFieldTypeSignature().asClassTypeSignature().type() == type) {
+      return toRenamedKmType(
+          typeSignature.asFieldTypeSignature().asClassTypeSignature(), appView, lens);
+    }
+
     String classifier = toRenamedClassifier(type, appView, lens);
     if (classifier == null) {
       return null;
@@ -98,12 +117,74 @@
     //   and/or why wiping out flags works for KmType but not KmFunction?!
     KmType kmType = new KmType(flagsOf());
     kmType.visitClass(classifier);
-    // TODO(b/70169921): Need to set arguments as type parameter.
-    //  E.g., for kotlin/Function1<P1, R>, P1 and R are recorded inside KmType.arguments, which
-    //  enables kotlinc to resolve `this` type and return type of the lambda.
+    // TODO(b/70169921): Can be generalized too, like ArrayTypeSignature.Converter ?
+    // E.g., java.lang.String[] -> KmType(kotlin/Array, KmTypeProjection(OUT, kotlin/String))
+    if (type.isArrayType() && !type.isPrimitiveArrayType()) {
+      DexType elementType = type.toArrayElementType(appView.dexItemFactory());
+      KmType argumentType = toRenamedKmType(elementType, null, appView, lens);
+      kmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType));
+    }
     return kmType;
   }
 
+  static KmType toRenamedKmType(
+      ClassTypeSignature classTypeSignature,
+      AppView<AppInfoWithLiveness> appView,
+      NamingLens lens) {
+    return classTypeSignature.convert(
+        new ClassTypeSignatureToRenamedKmTypeConverter(appView, lens));
+  }
+
+  static class ClassTypeSignatureToRenamedKmTypeConverter
+      implements ClassTypeSignature.Converter<KmType> {
+
+    private final AppView<AppInfoWithLiveness> appView;
+    private final NamingLens lens;
+
+    ClassTypeSignatureToRenamedKmTypeConverter(
+        AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+      this.appView = appView;
+      this.lens = lens;
+    }
+
+    @Override
+    public KmType init() {
+      return new KmType(flagsOf());
+    }
+
+    @Override
+    public KmType visitType(DexType type, KmType result) {
+      if (result == null) {
+        return result;
+      }
+      String classifier = toRenamedClassifier(type, appView, lens);
+      if (classifier == null) {
+        return null;
+      }
+      result.visitClass(classifier);
+      return result;
+    }
+
+    @Override
+    public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) {
+      if (result == null) {
+        return result;
+      }
+      if (typeArgument.isClassTypeSignature()) {
+        KmType argumentType = typeArgument.asClassTypeSignature().convert(this);
+        result.getArguments().add(new KmTypeProjection(KmVariance.INVARIANT, argumentType));
+      }
+      // TODO(b/70169921): for TypeVariableSignature, there is KmType::visitTypeParameter.
+      return result;
+    }
+
+    @Override
+    public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) {
+      // Do nothing
+      return result;
+    }
+  }
+
   static KmConstructor toRenamedKmConstructor(
       DexEncodedMethod method,
       AppView<AppInfoWithLiveness> appView,
@@ -115,8 +196,9 @@
     }
     KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags());
     JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method));
+    MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
     List<KmValueParameter> parameters = kmConstructor.getValueParameters();
-    if (!populateKmValueParameters(parameters, method, appView, lens)) {
+    if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
       return null;
     }
     return kmConstructor;
@@ -145,23 +227,34 @@
             : method.accessFlags.getAsKotlinFlags();
     KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString());
     JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod));
-    KmType kmReturnType = toRenamedKmType(method.method.proto.returnType, appView, lens);
+
+    // TODO(b/129925954): Should this be integrated as part of DexDefinition instead of parsing it
+    //  on demand? That may require utils to map internal encoding of signature back to
+    //  corresponding backend definitions, like DexAnnotation (DEX) or Signature attribute (CF).
+    MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
+
+    DexType returnType = method.method.proto.returnType;
+    TypeSignature returnSignature = signature.returnType();
+    KmType kmReturnType = toRenamedKmType(returnType, returnSignature, appView, lens);
     if (kmReturnType == null) {
       return null;
     }
     kmFunction.setReturnType(kmReturnType);
+
     if (method.isKotlinExtensionFunction()) {
       assert method.method.proto.parameters.values.length > 0
           : method.method.toSourceString();
-      KmType kmReceiverType =
-          toRenamedKmType(method.method.proto.parameters.values[0], appView, lens);
+      DexType receiverType = method.method.proto.parameters.values[0];
+      TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+      KmType kmReceiverType = toRenamedKmType(receiverType, receiverSignature, appView, lens);
       if (kmReceiverType == null) {
         return null;
       }
       kmFunction.setReceiverParameterType(kmReceiverType);
     }
+
     List<KmValueParameter> parameters = kmFunction.getValueParameters();
-    if (!populateKmValueParameters(parameters, method, appView, lens)) {
+    if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
       return null;
     }
     return kmFunction;
@@ -170,6 +263,7 @@
   private static boolean populateKmValueParameters(
       List<KmValueParameter> parameters,
       DexEncodedMethod method,
+      MethodTypeSignature signature,
       AppView<AppInfoWithLiveness> appView,
       NamingLens lens) {
     boolean isExtension = method.isKotlinExtensionFunction();
@@ -179,9 +273,15 @@
       String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
       KotlinValueParameterInfo valueParameterInfo =
           method.getKotlinMemberInfo().getValueParameterInfo(isExtension ? i - 1 : i);
+      TypeSignature parameterTypeSignature = signature.getParameterTypeSignature(i);
       KmValueParameter kmValueParameter =
           toRewrittenKmValueParameter(
-              valueParameterInfo, parameterType, parameterName, appView, lens);
+              valueParameterInfo,
+              parameterType,
+              parameterTypeSignature,
+              parameterName,
+              appView,
+              lens);
       if (kmValueParameter == null) {
         return false;
       }
@@ -193,10 +293,11 @@
   private static KmValueParameter toRewrittenKmValueParameter(
       KotlinValueParameterInfo valueParameterInfo,
       DexType parameterType,
+      TypeSignature parameterTypeSignature,
       String candidateParameterName,
       AppView<AppInfoWithLiveness> appView,
       NamingLens lens) {
-    KmType kmParamType = toRenamedKmType(parameterType, appView, lens);
+    KmType kmParamType = toRenamedKmType(parameterType, parameterTypeSignature, appView, lens);
     if (kmParamType == null) {
       return null;
     }
@@ -204,17 +305,22 @@
     String name = valueParameterInfo != null ? valueParameterInfo.name : candidateParameterName;
     KmValueParameter kmValueParameter = new KmValueParameter(flag, name);
     kmValueParameter.setType(kmParamType);
+
     if (valueParameterInfo != null && valueParameterInfo.isVararg) {
       if (!parameterType.isArrayType()) {
         return null;
       }
-      DexType elementType = parameterType.toBaseType(appView.dexItemFactory());
-      KmType kmElementType = toRenamedKmType(elementType, appView, lens);
+      DexType elementType = parameterType.toArrayElementType(appView.dexItemFactory());
+      TypeSignature elementSignature =
+          parameterTypeSignature != null
+              ? parameterTypeSignature.toArrayElementTypeSignature(appView) : null;
+      KmType kmElementType = toRenamedKmType(elementType, elementSignature, appView, lens);
       if (kmElementType == null) {
         return null;
       }
       kmValueParameter.setVarargElementType(kmElementType);
     }
+
     return kmValueParameter;
   }
 
@@ -363,7 +469,9 @@
         if (canChangePropertyName && renamedField.name != field.field.name) {
           renamedPropertyName = renamedField.name.toString();
         }
-        kmPropertyType = toRenamedKmType(field.field.type, appView, lens);
+        FieldTypeSignature signature =
+            GenericSignature.Parser.toFieldTypeSignature(field, appView);
+        kmPropertyType = toRenamedKmType(field.field.type, signature, appView, lens);
         if (kmPropertyType != null) {
           kmProperty.setReturnType(kmPropertyType);
         }
@@ -378,24 +486,31 @@
       if (criteria == GetterSetterCriteria.MET) {
         assert getter != null
             : "checkGetterCriteria: " + this.toString();
+        MethodTypeSignature signature =
+            GenericSignature.Parser.toMethodTypeSignature(getter, appView);
         if (isExtension) {
           assert getter.method.proto.parameters.size() == 1
               : "checkGetterCriteria: " + this.toString();
-          kmReceiverType = toRenamedKmType(getter.method.proto.parameters.values[0], appView, lens);
+          TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+          kmReceiverType = toRenamedKmType(
+              getter.method.proto.parameters.values[0], receiverSignature, appView, lens);
           if (kmReceiverType != null) {
             kmProperty.setReceiverParameterType(kmReceiverType);
           }
         }
+
+        DexType returnType = getter.method.proto.returnType;
+        TypeSignature returnSignature = signature.returnType();
         if (kmPropertyType == null) {
           // The property type is not set yet.
-          kmPropertyType = toRenamedKmType(getter.method.proto.returnType, appView, lens);
+          kmPropertyType = toRenamedKmType(returnType, returnSignature, appView, lens);
           if (kmPropertyType != null) {
             kmProperty.setReturnType(kmPropertyType);
           }
         } else {
           // If property type is set already (via backing field), make sure it's consistent.
           KmType kmPropertyTypeFromGetter =
-              toRenamedKmType(getter.method.proto.returnType, appView, lens);
+              toRenamedKmType(returnType, returnSignature, appView, lens);
           if (!getDescriptorFromKmType(kmPropertyType)
               .equals(getDescriptorFromKmType(kmPropertyTypeFromGetter))) {
             return null;
@@ -422,12 +537,15 @@
       if (criteria == GetterSetterCriteria.MET) {
         assert setter != null && setter.method.proto.parameters.size() >= 1
             : "checkSetterCriteria: " + this.toString();
+        MethodTypeSignature signature =
+            GenericSignature.Parser.toMethodTypeSignature(setter, appView);
         if (isExtension) {
           assert setter.method.proto.parameters.size() == 2
               : "checkSetterCriteria: " + this.toString();
+          DexType receiverType = setter.method.proto.parameters.values[0];
+          TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
           if (kmReceiverType == null) {
-            kmReceiverType =
-                toRenamedKmType(setter.method.proto.parameters.values[0], appView, lens);
+            kmReceiverType = toRenamedKmType(receiverType, receiverSignature, appView, lens);
             if (kmReceiverType != null) {
               kmProperty.setReceiverParameterType(kmReceiverType);
             }
@@ -435,25 +553,28 @@
             // If the receiver type for the extension property is set already (via getter),
             // make sure it's consistent.
             KmType kmReceiverTypeFromSetter =
-                toRenamedKmType(setter.method.proto.parameters.values[0], appView, lens);
+                toRenamedKmType(receiverType, receiverSignature, appView, lens);
             if (!getDescriptorFromKmType(kmReceiverType)
                 .equals(getDescriptorFromKmType(kmReceiverTypeFromSetter))) {
               return null;
             }
           }
         }
+
         int valueIndex = isExtension ? 1 : 0;
         DexType valueType = setter.method.proto.parameters.values[valueIndex];
+        TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex);
         if (kmPropertyType == null) {
           // The property type is not set yet.
-          kmPropertyType = toRenamedKmType(valueType, appView, lens);
+          kmPropertyType = toRenamedKmType(valueType, valueSignature, appView, lens);
           if (kmPropertyType != null) {
             kmProperty.setReturnType(kmPropertyType);
           }
         } else {
           // If property type is set already (via either backing field or getter),
           // make sure it's consistent.
-          KmType kmPropertyTypeFromSetter = toRenamedKmType(valueType, appView, lens);
+          KmType kmPropertyTypeFromSetter =
+              toRenamedKmType(valueType, valueSignature, appView, lens);
           if (!getDescriptorFromKmType(kmPropertyType)
               .equals(getDescriptorFromKmType(kmPropertyTypeFromSetter))) {
             return null;
@@ -461,8 +582,8 @@
         }
         KotlinValueParameterInfo valueParameterInfo =
             setter.getKotlinMemberInfo().getValueParameterInfo(valueIndex);
-        KmValueParameter kmValueParameter =
-            toRewrittenKmValueParameter(valueParameterInfo, valueType, "value", appView, lens);
+        KmValueParameter kmValueParameter = toRewrittenKmValueParameter(
+            valueParameterInfo, valueType, valueSignature, "value", appView, lens);
         if (kmValueParameter != null) {
           kmProperty.setSetterParameter(kmValueParameter);
         }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
index 54675ca..f5d26ae 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -7,22 +7,22 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
 import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeProjectionSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
 import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.nio.file.Path;
@@ -75,27 +75,25 @@
             // Keep LibKt and applyMap function, along with applyMap$default
             .addKeepRules("-keep class **.LibKt { *** applyMap*(...); }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+            .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+            .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
             .compile()
             .inspect(this::inspect)
             .writeToZip();
 
-    ProcessResult kotlinTestCompileResult =
+    Path output =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
             .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/default_value_app", "main"))
             .setOutputPath(temp.newFolder().toPath())
-            // TODO(b/70169921): update to just .compile() once fixed.
-            .compileRaw();
+            .compile();
 
-    // TODO(b/70169921): should be able to compile!
-    assertNotEquals(0, kotlinTestCompileResult.exitCode);
-    assertThat(
-        kotlinTestCompileResult.stderr,
-        containsString("type mismatch: inferred type is kotlin.collections.Map<String, String"));
-    assertThat(
-        kotlinTestCompileResult.stderr, containsString("but java.util.Map<K, V> was expected"));
-    assertThat(
-        kotlinTestCompileResult.stderr, not(containsString("no value passed for parameter 'p2'")));
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt")
+        .assertSuccessWithOutputLines("a", "b", "c");
   }
 
   private void inspect(CodeInspector inspector) {
@@ -116,12 +114,23 @@
     KmPackageSubject kmPackage = libKt.getKmPackage();
     assertThat(kmPackage, isPresent());
 
+    // String applyMap(Map<String, String>, (default) String)
     KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("applyMap");
     assertThat(kmFunction, isExtensionFunction());
     List<KmValueParameterSubject> valueParameters = kmFunction.valueParameters();
     assertEquals(2, valueParameters.size());
-    // TODO(b/70169921): inspect 1st arg is Map with correct type parameter.
-    KmValueParameterSubject valueParameter = valueParameters.get(1);
+
+    KmValueParameterSubject valueParameter = valueParameters.get(0);
+    assertFalse(valueParameter.declaresDefaultValue());
+    assertEquals("Lkotlin/collections/Map;", valueParameter.type().descriptor());
+    List<KmTypeProjectionSubject> typeArguments = valueParameter.type().typeArguments();
+    assertEquals(2, typeArguments.size());
+    KmTypeSubject typeArgument = typeArguments.get(0).type();
+    assertEquals("Lkotlin/String;", typeArgument.descriptor());
+    typeArgument = typeArguments.get(1).type();
+    assertEquals("Lkotlin/String;", typeArgument.descriptor());
+
+    valueParameter = valueParameters.get(1);
     assertTrue(valueParameter.declaresDefaultValue());
     assertEquals("Lkotlin/String;", valueParameter.type().descriptor());
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index 1bf7552..ea12b9e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -7,21 +7,22 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
 import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeProjectionSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
 import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.nio.file.Path;
@@ -75,24 +76,25 @@
             // Keep LibKt, along with bar function.
             .addKeepRules("-keep class **.LibKt { *** bar(...); }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+            .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+            .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
             .compile()
             .inspect(this::inspect)
             .writeToZip();
 
-    ProcessResult kotlinTestCompileResult =
+    Path output =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
             .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main"))
             .setOutputPath(temp.newFolder().toPath())
-            // TODO(b/70169921): update to just .compile() once fixed.
-            .compileRaw();
+            .compile();
 
-    // TODO(b/70169921): should be able to compile!
-    assertNotEquals(0, kotlinTestCompileResult.exitCode);
-    assertThat(
-        kotlinTestCompileResult.stderr,
-        not(containsString("type mismatch: inferred type is String but Array<T> was expected")));
-    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt")
+        .assertSuccessWithOutputLines("SomeClass::R8");
   }
 
   private void inspect(CodeInspector inspector) {
@@ -118,13 +120,32 @@
     KmPackageSubject kmPackage = libKt.getKmPackage();
     assertThat(kmPackage, isPresent());
 
+    // Unit bar(vararg String, (SomeClass, String) -> Unit)
     KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("bar");
     assertThat(kmFunction, not(isExtensionFunction()));
     List<KmValueParameterSubject> valueParameters = kmFunction.valueParameters();
     assertEquals(2, valueParameters.size());
+
     KmValueParameterSubject valueParameter = valueParameters.get(0);
     assertTrue(valueParameter.isVararg());
     assertEquals("Lkotlin/String;", valueParameter.varargElementType().descriptor());
-    // TODO(b/70169921): inspect 2nd arg is lambda with correct type parameter.
+
+    assertEquals("Lkotlin/Array;", valueParameter.type().descriptor());
+    List<KmTypeProjectionSubject> typeArguments = valueParameter.type().typeArguments();
+    assertEquals(1, typeArguments.size());
+    KmTypeSubject typeArgument = typeArguments.get(0).type();
+    assertEquals("Lkotlin/String;", typeArgument.descriptor());
+
+    valueParameter = valueParameters.get(1);
+    assertFalse(valueParameter.isVararg());
+    typeArguments = valueParameter.type().typeArguments();
+    assertEquals(3, typeArguments.size());
+
+    typeArgument = typeArguments.get(0).type();
+    assertEquals(cls.getFinalDescriptor(), typeArgument.descriptor());
+    typeArgument = typeArguments.get(1).type();
+    assertEquals("Lkotlin/String;", typeArgument.descriptor());
+    typeArgument = typeArguments.get(2).type();
+    assertEquals("Lkotlin/Unit;", typeArgument.descriptor());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
index 28eff88..cdf57aa 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
@@ -54,18 +54,18 @@
   public KmTypeSubject receiverParameterType() {
     KmType kmType = kmFunction.getReceiverParameterType();
     assert !isExtension() || kmType != null;
-    return kmType == null ? null : new KmTypeSubject(kmType);
+    return kmType == null ? null : new KmTypeSubject(codeInspector, kmType);
   }
 
   @Override
   public List<KmValueParameterSubject> valueParameters() {
     return kmFunction.getValueParameters().stream()
-        .map(KmValueParameterSubject::new)
+        .map(kmValueParameter -> new KmValueParameterSubject(codeInspector, kmValueParameter))
         .collect(Collectors.toList());
   }
 
   @Override
   public KmTypeSubject returnType() {
-    return new KmTypeSubject(kmFunction.getReturnType());
+    return new KmTypeSubject(codeInspector, kmFunction.getReturnType());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
new file mode 100644
index 0000000..790ecd2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.codeinspector;
+
+import com.android.tools.r8.errors.Unreachable;
+import kotlinx.metadata.KmTypeProjection;
+
+public class KmTypeProjectionSubject extends Subject {
+  private final CodeInspector codeInspector;
+  private final KmTypeProjection kmTypeProjection;
+
+  KmTypeProjectionSubject(CodeInspector codeInspector, KmTypeProjection kmTypeProjection) {
+    this.codeInspector = codeInspector;
+    this.kmTypeProjection = kmTypeProjection;
+  }
+
+  public KmTypeSubject type() {
+    return new KmTypeSubject(codeInspector, kmTypeProjection.getType());
+  }
+
+  @Override
+  public boolean isPresent() {
+    return true;
+  }
+
+  @Override
+  public boolean isRenamed() {
+    return type().isRenamed();
+  }
+
+  @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if a type argument is synthetic");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
index aa52c67..5d7ec81 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
@@ -6,15 +6,20 @@
 import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.Box;
+import java.util.List;
+import java.util.stream.Collectors;
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.KmTypeVisitor;
 
 public class KmTypeSubject extends Subject {
+  private final CodeInspector codeInspector;
   private final KmType kmType;
 
-  KmTypeSubject(KmType kmType) {
+  KmTypeSubject(CodeInspector codeInspector, KmType kmType) {
     assert kmType != null;
+    this.codeInspector = codeInspector;
     this.kmType = kmType;
   }
 
@@ -46,6 +51,12 @@
     return getDescriptorFromKmType(kmType);
   }
 
+  public List<KmTypeProjectionSubject> typeArguments() {
+    return kmType.getArguments().stream()
+        .map(kmTypeProjection -> new KmTypeProjectionSubject(codeInspector, kmTypeProjection))
+        .collect(Collectors.toList());
+  }
+
   @Override
   public boolean isPresent() {
     return true;
@@ -53,7 +64,8 @@
 
   @Override
   public boolean isRenamed() {
-    throw new Unreachable("Cannot determine if a type is renamed");
+    ClassSubject classSubject = codeInspector.clazz(Reference.classFromDescriptor(descriptor()));
+    return classSubject.isRenamed();
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
index 1f96b7d..61a2531 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
@@ -9,21 +9,23 @@
 import kotlinx.metadata.KmValueParameter;
 
 public class KmValueParameterSubject extends Subject {
+  private final CodeInspector codeInspector;
   private final KmValueParameter kmValueParameter;
 
-  KmValueParameterSubject(KmValueParameter kmValueParameter) {
+  KmValueParameterSubject(CodeInspector codeInspector, KmValueParameter kmValueParameter) {
+    this.codeInspector = codeInspector;
     this.kmValueParameter = kmValueParameter;
   }
 
   public KmTypeSubject type() {
-    return new KmTypeSubject(kmValueParameter.getType());
+    return new KmTypeSubject(codeInspector, kmValueParameter.getType());
   }
 
   public KmTypeSubject varargElementType() {
     if (!isVararg()) {
       return null;
     }
-    return new KmTypeSubject(kmValueParameter.getVarargElementType());
+    return new KmTypeSubject(codeInspector, kmValueParameter.getVarargElementType());
   }
 
   public boolean isVararg() {