Convert platform-related providers to use BuiltinProvider instead of NativeProvider.

PiperOrigin-RevId: 290784335
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintSettingInfo.java b/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintSettingInfo.java
index f784467..e8bc654 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintSettingInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintSettingInfo.java
@@ -18,8 +18,8 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.BuiltinProvider;
 import com.google.devtools.build.lib.packages.NativeInfo;
-import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 import com.google.devtools.build.lib.skylarkbuildapi.platform.ConstraintSettingInfoApi;
@@ -34,9 +34,9 @@
   /** Name used in Skylark for accessing this provider. */
   public static final String SKYLARK_NAME = "ConstraintSettingInfo";
 
-  /** Skylark constructor and identifier for this provider. */
-  public static final NativeProvider<ConstraintSettingInfo> PROVIDER =
-      new NativeProvider<ConstraintSettingInfo>(ConstraintSettingInfo.class, SKYLARK_NAME) {};
+  /** Provider singleton constant. */
+  public static final BuiltinProvider<ConstraintSettingInfo> PROVIDER =
+      new BuiltinProvider<ConstraintSettingInfo>(SKYLARK_NAME, ConstraintSettingInfo.class) {};
 
   private final Label label;
   @Nullable private final Label defaultConstraintValueLabel;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintValueInfo.java b/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintValueInfo.java
index 40856c4..d769db7 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintValueInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/platform/ConstraintValueInfo.java
@@ -17,8 +17,8 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.BuiltinProvider;
 import com.google.devtools.build.lib.packages.NativeInfo;
-import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 import com.google.devtools.build.lib.skylarkbuildapi.platform.ConstraintValueInfoApi;
@@ -33,9 +33,9 @@
   /** Name used in Skylark for accessing this provider. */
   public static final String SKYLARK_NAME = "ConstraintValueInfo";
 
-  /** Skylark constructor and identifier for this provider. */
-  public static final NativeProvider<ConstraintValueInfo> PROVIDER =
-      new NativeProvider<ConstraintValueInfo>(ConstraintValueInfo.class, SKYLARK_NAME) {};
+  /** Provider singleton constant. */
+  public static final BuiltinProvider<ConstraintValueInfo> PROVIDER =
+      new BuiltinProvider<ConstraintValueInfo>(SKYLARK_NAME, ConstraintValueInfo.class) {};
 
   private final ConstraintSettingInfo constraint;
   private final Label label;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformInfo.java b/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformInfo.java
index d8ccda7..27cd1f0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformInfo.java
@@ -20,12 +20,16 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.BuiltinProvider;
 import com.google.devtools.build.lib.packages.NativeInfo;
-import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 import com.google.devtools.build.lib.skylarkbuildapi.platform.PlatformInfoApi;
+import com.google.devtools.build.lib.syntax.Dict;
+import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.Printer;
+import com.google.devtools.build.lib.syntax.Sequence;
+import com.google.devtools.build.lib.syntax.Starlark;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.util.StringUtilities;
 import java.util.HashMap;
@@ -49,9 +53,49 @@
   /** Name used in Skylark for accessing this provider. */
   public static final String SKYLARK_NAME = "PlatformInfo";
 
-  /** Skylark constructor and identifier for this provider. */
-  public static final NativeProvider<PlatformInfo> PROVIDER =
-      new NativeProvider<PlatformInfo>(PlatformInfo.class, SKYLARK_NAME) {};
+  /** Provider singleton constant. */
+  public static final BuiltinProvider<PlatformInfo> PROVIDER = new Provider();
+
+  /** Provider for {@link ToolchainInfo} objects. */
+  private static class Provider extends BuiltinProvider<PlatformInfo>
+      implements PlatformInfoApi.Provider<
+          ConstraintSettingInfo, ConstraintValueInfo, PlatformInfo> {
+    private Provider() {
+      super(SKYLARK_NAME, PlatformInfo.class);
+    }
+
+    @Override
+    public PlatformInfo platformInfo(
+        Label label,
+        Object parentUnchecked,
+        Sequence<?> constraintValuesUnchecked,
+        Object execPropertiesUnchecked,
+        Location location)
+        throws EvalException {
+      PlatformInfo.Builder builder = PlatformInfo.builder();
+      builder.setLabel(label);
+      if (parentUnchecked != Starlark.NONE) {
+        builder.setParent((PlatformInfo) parentUnchecked);
+      }
+      if (!constraintValuesUnchecked.isEmpty()) {
+        builder.addConstraints(
+            constraintValuesUnchecked.getContents(ConstraintValueInfo.class, "constraint_values"));
+      }
+      if (execPropertiesUnchecked != null) {
+        Map<String, String> execProperties =
+            Dict.castSkylarkDictOrNoneToDict(
+                execPropertiesUnchecked, String.class, String.class, "exec_properties");
+        builder.setExecProperties(ImmutableMap.copyOf(execProperties));
+      }
+      builder.setLocation(location);
+
+      try {
+        return builder.build();
+      } catch (DuplicateConstraintException | ExecPropertiesException e) {
+        throw new EvalException(location, e);
+      }
+    }
+  }
 
   private final Label label;
   private final ConstraintCollection constraints;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/platform/ToolchainTypeInfo.java b/src/main/java/com/google/devtools/build/lib/analysis/platform/ToolchainTypeInfo.java
index 702d582..fa1e8a0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/platform/ToolchainTypeInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/platform/ToolchainTypeInfo.java
@@ -17,8 +17,8 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.BuiltinProvider;
 import com.google.devtools.build.lib.packages.NativeInfo;
-import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 import com.google.devtools.build.lib.skylarkbuildapi.platform.ToolchainTypeInfoApi;
@@ -32,10 +32,9 @@
   /** Name used in Skylark for accessing this provider. */
   public static final String SKYLARK_NAME = "ToolchainTypeInfo";
 
-  /** Skylark constructor and identifier for this provider. */
-  @AutoCodec
-  public static final NativeProvider<ToolchainTypeInfo> PROVIDER =
-      new NativeProvider<ToolchainTypeInfo>(ToolchainTypeInfo.class, SKYLARK_NAME) {};
+  /** Provider singleton constant. */
+  public static final BuiltinProvider<ToolchainTypeInfo> PROVIDER =
+      new BuiltinProvider<ToolchainTypeInfo>(SKYLARK_NAME, ToolchainTypeInfo.class) {};
 
   private final Label typeLabel;
 
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform/PlatformInfoApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform/PlatformInfoApi.java
index 277fb26..08a4944 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform/PlatformInfoApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform/PlatformInfoApi.java
@@ -15,10 +15,17 @@
 package com.google.devtools.build.lib.skylarkbuildapi.platform;
 
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.skylarkbuildapi.core.ProviderApi;
 import com.google.devtools.build.lib.skylarkbuildapi.core.StructApi;
+import com.google.devtools.build.lib.skylarkinterface.Param;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkConstructor;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
+import com.google.devtools.build.lib.syntax.Dict;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.Sequence;
 import com.google.devtools.build.lib.syntax.StarlarkSemantics.FlagIdentifier;
 import java.util.Map;
 
@@ -67,4 +74,61 @@
       structField = true,
       enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_PLATFORM_API)
   Map<String, String> execProperties();
+
+  /** Provider for {@link PlatformInfoApi} objects. */
+  @SkylarkModule(name = "Provider", documented = false, doc = "")
+  interface Provider<
+          ConstraintSettingInfoT extends ConstraintSettingInfoApi,
+          ConstraintValueInfoT extends ConstraintValueInfoApi,
+          PlatformInfoT extends PlatformInfoApi<ConstraintSettingInfoT, ConstraintValueInfoT>>
+      extends ProviderApi {
+
+    @SkylarkCallable(
+        name = "PlatformInfo",
+        doc = "The <code>PlatformInfo</code> constructor.",
+        documented = false,
+        parameters = {
+          @Param(
+              name = "label",
+              type = Label.class,
+              named = true,
+              positional = false,
+              doc = "The label for this platform."),
+          @Param(
+              name = "parent",
+              type = PlatformInfoApi.class,
+              defaultValue = "None",
+              named = true,
+              positional = false,
+              noneable = true,
+              doc = "The parent of this platform."),
+          @Param(
+              name = "constraint_values",
+              type = Sequence.class,
+              defaultValue = "[]",
+              generic1 = ConstraintValueInfoApi.class,
+              named = true,
+              positional = false,
+              doc = "The constraint values for the platform"),
+          @Param(
+              name = "exec_properties",
+              type = Dict.class,
+              defaultValue = "None",
+              named = true,
+              positional = false,
+              noneable = true,
+              doc = "The exec properties for the platform.")
+        },
+        selfCall = true,
+        useLocation = true,
+        enableOnlyWithFlag = FlagIdentifier.EXPERIMENTAL_PLATFORM_API)
+    @SkylarkConstructor(objectType = PlatformInfoApi.class, receiverNameForDoc = "PlatformInfo")
+    PlatformInfoT platformInfo(
+        Label label,
+        Object parent,
+        Sequence<?> constraintValues,
+        Object execProperties,
+        Location location)
+        throws EvalException;
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/platform/PlatformInfoTest.java b/src/test/java/com/google/devtools/build/lib/analysis/platform/PlatformInfoTest.java
index f404160..13337c3 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/platform/PlatformInfoTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/platform/PlatformInfoTest.java
@@ -19,6 +19,7 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.testing.EqualsTester;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -319,4 +320,151 @@
     assertThat(platformInfo).isNotNull();
     assertThat(platformInfo.execProperties()).containsExactly("p1", "keep", "p3", "child");
   }
+
+  @Test
+  public void platformInfo_constructor() throws Exception {
+    scratch.file(
+        "test/platform/my_platform.bzl",
+        "def _impl(ctx):",
+        "  constraints = [val[platform_common.ConstraintValueInfo] "
+            + "for val in ctx.attr.constraints]",
+        "  platform = platform_common.PlatformInfo(",
+        "      label = ctx.label, constraint_values = constraints)",
+        "  return [platform]",
+        "my_platform = rule(",
+        "  implementation = _impl,",
+        "  attrs = {",
+        "    'constraints': attr.label_list(providers = [platform_common.ConstraintValueInfo])",
+        "  }",
+        ")");
+    scratch.file(
+        "test/constraint/BUILD",
+        "constraint_setting(name = 'basic')",
+        "constraint_value(name = 'foo',",
+        "    constraint_setting = ':basic',",
+        ")");
+    scratch.file(
+        "test/platform/BUILD",
+        "load('//test/platform:my_platform.bzl', 'my_platform')",
+        "my_platform(name = 'custom',",
+        "    constraints = [",
+        "       '//test/constraint:foo',",
+        "    ],",
+        ")");
+
+    setSkylarkSemanticsOptions("--experimental_platforms_api");
+    ConfiguredTarget platform = getConfiguredTarget("//test/platform:custom");
+    assertThat(platform).isNotNull();
+
+    PlatformInfo provider = PlatformProviderUtils.platform(platform);
+    assertThat(provider).isNotNull();
+    assertThat(provider.label()).isEqualTo(makeLabel("//test/platform:custom"));
+    ConstraintSettingInfo constraintSetting =
+        ConstraintSettingInfo.create(makeLabel("//test/constraint:basic"));
+    ConstraintValueInfo constraintValue =
+        ConstraintValueInfo.create(constraintSetting, makeLabel("//test/constraint:foo"));
+    assertThat(provider.constraints().get(constraintSetting)).isEqualTo(constraintValue);
+  }
+
+  @Test
+  public void platformInfo_constructor_parent() throws Exception {
+    scratch.file(
+        "test/platform/my_platform.bzl",
+        "def _impl(ctx):",
+        "  constraints = [val[platform_common.ConstraintValueInfo] "
+            + "for val in ctx.attr.constraints]",
+        "  platform = platform_common.PlatformInfo(",
+        "      label = ctx.label, constraint_values = constraints)",
+        "  return [platform]",
+        "my_platform = rule(",
+        "  implementation = _impl,",
+        "  attrs = {",
+        "    'constraints': attr.label_list(providers = [platform_common.ConstraintValueInfo])",
+        "  }",
+        ")");
+    scratch.file(
+        "test/constraint/BUILD",
+        "constraint_setting(name = 'basic')",
+        "constraint_setting(name = 'complex')",
+        "constraint_value(name = 'foo',",
+        "    constraint_setting = ':basic',",
+        ")",
+        "constraint_value(name = 'bar',",
+        "    constraint_setting = ':basic',",
+        ")",
+        "constraint_value(name = 'baz',",
+        "    constraint_setting = ':complex',",
+        ")");
+    scratch.file(
+        "test/platform/BUILD",
+        "load('//test/platform:my_platform.bzl', 'my_platform')",
+        "platform(",
+        "    name='parent',",
+        "    constraint_values = [",
+        "       '//test/constraint:foo',",
+        "    ],",
+        ")",
+        "my_platform(name = 'custom',",
+        "    constraints = [",
+        "       '//test/constraint:bar',",
+        "       '//test/constraint:baz',",
+        "    ],",
+        ")");
+
+    setSkylarkSemanticsOptions("--experimental_platforms_api");
+    ConfiguredTarget platform = getConfiguredTarget("//test/platform:custom");
+    assertThat(platform).isNotNull();
+
+    PlatformInfo provider = PlatformProviderUtils.platform(platform);
+    assertThat(provider).isNotNull();
+    assertThat(provider.label()).isEqualTo(makeLabel("//test/platform:custom"));
+
+    // Check that overrides work.
+    ConstraintSettingInfo constraintSetting =
+        ConstraintSettingInfo.create(makeLabel("//test/constraint:basic"));
+    ConstraintValueInfo constraintValue =
+        ConstraintValueInfo.create(constraintSetting, makeLabel("//test/constraint:bar"));
+    assertThat(provider.constraints().get(constraintSetting)).isEqualTo(constraintValue);
+
+    // Check that inheritance works.
+    ConstraintSettingInfo constraintSetting2 =
+        ConstraintSettingInfo.create(makeLabel("//test/constraint:complex"));
+    ConstraintValueInfo constraintValue2 =
+        ConstraintValueInfo.create(constraintSetting2, makeLabel("//test/constraint:baz"));
+    assertThat(provider.constraints().get(constraintSetting2)).isEqualTo(constraintValue2);
+  }
+
+  @Test
+  public void platformInfo_constructor_error_duplicateConstraints() throws Exception {
+    scratch.file(
+        "test/platform/my_platform.bzl",
+        "def _impl(ctx):",
+        "  platform = platform_common.PlatformInfo()",
+        "  return [platform]",
+        "my_platform = rule(",
+        "  implementation = _impl,",
+        "  attrs = {",
+        "    'constraints': attr.label_list(providers = [platform_common.ConstraintValueInfo])",
+        "  }",
+        ")");
+    scratch.file(
+        "test/constraint/BUILD",
+        "constraint_setting(name = 'basic')",
+        "constraint_value(name = 'foo',",
+        "    constraint_setting = ':basic',",
+        ")");
+    setSkylarkSemanticsOptions("--experimental_platforms_api");
+    checkError(
+        "test/platform",
+        "custom",
+        "Label '//test/constraint:foo' is duplicated in the 'constraints' attribute of rule"
+            + " 'custom'",
+        "load('//test/platform:my_platform.bzl', 'my_platform')",
+        "my_platform(name = 'custom',",
+        "    constraints = [",
+        "       '//test/constraint:foo',",
+        "       '//test/constraint:foo',",
+        "    ],",
+        ")");
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SingleToolchainResolutionFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SingleToolchainResolutionFunctionTest.java
index ee39c29..77a2b5e 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SingleToolchainResolutionFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SingleToolchainResolutionFunctionTest.java
@@ -30,8 +30,8 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.packages.BuiltinProvider;
 import com.google.devtools.build.lib.packages.Info;
-import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.rules.platform.ToolchainTestCase;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
@@ -245,7 +245,7 @@
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends Info> T get(NativeProvider<T> provider) {
+    public <T extends Info> T get(BuiltinProvider<T> provider) {
       if (PlatformInfo.PROVIDER.equals(provider)) {
         return (T) this.platform;
       }