Separate Info object semantics from Struct object semantics.

Info objects are objects that are created by Provider instances, and Struct objects are created by calls to struct(). There's no real reason these two sets of semantics must be bound together.

This change demonstrates separated semantics by migrating ObjcProvider to be no longer a struct. This means that ObjcProvider no longer has methods to_json() and to_proto(); this should not be considered a breaking change, however, because prior to this change, calling those methods on an ObjcProvider would always result in a thrown error.

RELNOTES: None.
PiperOrigin-RevId: 209210876
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java
index 4e887e4..0e32ea5 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java
@@ -18,8 +18,8 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.BuiltinProvider;
-import com.google.devtools.build.lib.packages.Info;
 import com.google.devtools.build.lib.packages.SkylarkInfo;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.skylarkbuildapi.ActionsInfoProviderApi;
 import java.util.HashMap;
 import java.util.Map;
@@ -28,17 +28,18 @@
  * This provides a view over the actions that were created during the analysis of a rule
  * (not including actions for its transitive dependencies).
  */
-public final class ActionsProvider extends BuiltinProvider<Info> implements ActionsInfoProviderApi {
+public final class ActionsProvider extends BuiltinProvider<StructImpl>
+    implements ActionsInfoProviderApi {
 
   /** The ActionsProvider singleton instance. */
   public static final ActionsProvider INSTANCE = new ActionsProvider();
 
   public ActionsProvider() {
-    super("Actions", Info.class);
+    super("Actions", StructImpl.class);
   }
 
   /** Factory method for creating instances of the Actions provider. */
-  public static Info create(Iterable<ActionAnalysisMetadata> actions) {
+  public static StructImpl create(Iterable<ActionAnalysisMetadata> actions) {
     Map<Artifact, ActionAnalysisMetadata> map = new HashMap<>();
     for (ActionAnalysisMetadata action : actions) {
       for (Artifact artifact : action.getOutputs()) {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java
index b7d8e14..099392b 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java
@@ -32,7 +32,7 @@
 import com.google.devtools.build.lib.events.ExtendedEventHandler;
 import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.BuiltinProvider;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.packages.TriState;
@@ -94,7 +94,7 @@
    * Returns the list of declared providers (native and Skylark) of the specified Skylark key from a
    * set of transitive info collections.
    */
-  public static <T extends Info> Iterable<T> getProviders(
+  public static <T extends InfoInterface> Iterable<T> getProviders(
       Iterable<? extends TransitiveInfoCollection> prerequisites,
       final NativeProvider<T> skylarkKey) {
     ImmutableList.Builder<T> result = ImmutableList.builder();
@@ -111,7 +111,7 @@
    * Returns the list of declared providers (native and Skylark) of the specified Skylark key from a
    * set of transitive info collections.
    */
-  public static <T extends Info> Iterable<T> getProviders(
+  public static <T extends InfoInterface> Iterable<T> getProviders(
       Iterable<? extends TransitiveInfoCollection> prerequisites,
       final BuiltinProvider<T> skylarkKey) {
     ImmutableList.Builder<T> result = ImmutableList.builder();
@@ -133,8 +133,8 @@
   }
 
   /** Returns the iterable of collections that have the specified provider. */
-  public static <S extends TransitiveInfoCollection, C extends Info> Iterable<S> filterByProvider(
-      Iterable<S> prerequisites, final NativeProvider<C> provider) {
+  public static <S extends TransitiveInfoCollection, C extends InfoInterface> Iterable<S>
+      filterByProvider(Iterable<S> prerequisites, final NativeProvider<C> provider) {
     return Iterables.filter(prerequisites, target -> target.get(provider) != null);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java
index e99d134..a87bdde 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java
@@ -33,7 +33,7 @@
 import com.google.devtools.build.lib.packages.AspectClass;
 import com.google.devtools.build.lib.packages.AspectDescriptor;
 import com.google.devtools.build.lib.packages.AspectParameters;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
@@ -124,7 +124,7 @@
     }
   }
 
-  public Info get(Provider.Key key) {
+  public InfoInterface get(Provider.Key key) {
     return providers.getProvider(key);
   }
 
@@ -237,7 +237,7 @@
       return this;
     }
 
-    public Builder addSkylarkDeclaredProvider(Info declaredProvider)
+    public Builder addSkylarkDeclaredProvider(InfoInterface declaredProvider)
         throws EvalException {
       Provider constructor = declaredProvider.getProvider();
       if (!constructor.isExported()) {
@@ -248,11 +248,11 @@
       return this;
     }
 
-    private void addDeclaredProvider(Info declaredProvider) {
+    private void addDeclaredProvider(InfoInterface declaredProvider) {
       providers.put(declaredProvider);
     }
 
-    public Builder addNativeDeclaredProvider(Info declaredProvider) {
+    public Builder addNativeDeclaredProvider(InfoInterface declaredProvider) {
       Provider constructor = declaredProvider.getProvider();
       Preconditions.checkState(constructor.isExported());
       addDeclaredProvider(declaredProvider);
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
index 09b989f..15ad36a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
@@ -41,7 +41,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.TargetUtils;
@@ -277,7 +277,8 @@
     return this;
   }
 
-  private <T extends TransitiveInfoProvider> void maybeAddSkylarkLegacyProvider(Info value) {
+  private <T extends TransitiveInfoProvider> void maybeAddSkylarkLegacyProvider(
+      InfoInterface value) {
     if (value.getProvider() instanceof NativeProvider.WithLegacySkylarkName) {
       addSkylarkTransitiveInfo(
           ((NativeProvider.WithLegacySkylarkName) value.getProvider()).getSkylarkName(), value);
@@ -303,9 +304,9 @@
    * <p>Has special handling for {@link OutputGroupInfo}: that provider is not added from
    * Skylark directly, instead its outpuyt groups are added.
    *
-   * <p>Use {@link #addNativeDeclaredProvider(Info)} in definitions of native rules.
+   * <p>Use {@link #addNativeDeclaredProvider(InfoInterface)} in definitions of native rules.
    */
-  public RuleConfiguredTargetBuilder addSkylarkDeclaredProvider(Info provider)
+  public RuleConfiguredTargetBuilder addSkylarkDeclaredProvider(InfoInterface provider)
       throws EvalException {
     Provider constructor = provider.getProvider();
     if (!constructor.isExported()) {
@@ -327,10 +328,10 @@
    * Adds "declared providers" defined in native code to the rule. Use this method for declared
    * providers in definitions of native rules.
    *
-   * <p>Use {@link #addSkylarkDeclaredProvider(Info)} for Skylark rule implementations.
+   * <p>Use {@link #addSkylarkDeclaredProvider(InfoInterface)} for Skylark rule implementations.
    */
-  public RuleConfiguredTargetBuilder addNativeDeclaredProviders(Iterable<Info> providers) {
-    for (Info provider : providers) {
+  public RuleConfiguredTargetBuilder addNativeDeclaredProviders(Iterable<InfoInterface> providers) {
+    for (InfoInterface provider : providers) {
       addNativeDeclaredProvider(provider);
     }
     return this;
@@ -340,9 +341,9 @@
    * Adds a "declared provider" defined in native code to the rule. Use this method for declared
    * providers in definitions of native rules.
    *
-   * <p>Use {@link #addSkylarkDeclaredProvider(Info)} for Skylark rule implementations.
+   * <p>Use {@link #addSkylarkDeclaredProvider(InfoInterface)} for Skylark rule implementations.
    */
-  public RuleConfiguredTargetBuilder addNativeDeclaredProvider(Info provider) {
+  public RuleConfiguredTargetBuilder addNativeDeclaredProvider(InfoInterface provider) {
     Provider constructor = provider.getProvider();
     Preconditions.checkState(constructor.isExported());
     providersBuilder.put(provider);
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 0cccfb8..7641833 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -76,6 +76,7 @@
 import com.google.devtools.build.lib.packages.FilesetEntry;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
 import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.InputFile;
 import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.packages.OutputFile;
@@ -971,7 +972,7 @@
    * Returns all the declared providers (native and Skylark) for the specified constructor under the
    * specified attribute of this target in the BUILD file.
    */
-  public <T extends Info> Iterable<T> getPrerequisites(
+  public <T extends InfoInterface> Iterable<T> getPrerequisites(
       String attributeName, Mode mode, final NativeProvider<T> skylarkKey) {
     return AnalysisUtils.getProviders(getPrerequisites(attributeName, mode), skylarkKey);
   }
@@ -980,7 +981,7 @@
    * Returns all the declared providers (native and Skylark) for the specified constructor under the
    * specified attribute of this target in the BUILD file.
    */
-  public <T extends Info> Iterable<T> getPrerequisites(
+  public <T extends InfoInterface> Iterable<T> getPrerequisites(
       String attributeName, Mode mode, final BuiltinProvider<T> skylarkKey) {
     return AnalysisUtils.getProviders(getPrerequisites(attributeName, mode), skylarkKey);
   }
@@ -991,7 +992,7 @@
    * TransitiveInfoCollection under the specified attribute.
    */
   @Nullable
-  public <T extends Info> T getPrerequisite(
+  public <T extends InfoInterface> T getPrerequisite(
       String attributeName, Mode mode, final NativeProvider<T> skylarkKey) {
     TransitiveInfoCollection prerequisite = getPrerequisite(attributeName, mode);
     return prerequisite == null ? null : prerequisite.get(skylarkKey);
@@ -1003,7 +1004,7 @@
    * TransitiveInfoCollection under the specified attribute.
    */
   @Nullable
-  public <T extends Info> T getPrerequisite(
+  public <T extends InfoInterface> T getPrerequisite(
       String attributeName, Mode mode, final BuiltinProvider<T> skylarkKey) {
     TransitiveInfoCollection prerequisite = getPrerequisite(attributeName, mode);
     return prerequisite == null ? null : prerequisite.get(skylarkKey);
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviderCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviderCollection.java
index 7e2a59b..c30e12a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviderCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviderCollection.java
@@ -15,7 +15,7 @@
 package com.google.devtools.build.lib.analysis;
 
 import com.google.devtools.build.lib.packages.BuiltinProvider;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
@@ -40,7 +40,7 @@
    * <p>Use {@link #get(NativeProvider)} for native providers.
    */
   @Nullable
-  Info get(Provider.Key providerKey);
+  InfoInterface get(Provider.Key providerKey);
 
   /**
    * Returns the native declared provider requested, or null, if the information is not found.
@@ -48,7 +48,7 @@
    * <p>Type-safe version of {@link #get(Provider.Key)} for native providers.
    */
   @Nullable
-  default <T extends Info> T get(NativeProvider<T> provider) {
+  default <T extends InfoInterface> T get(NativeProvider<T> provider) {
     return provider.getValueClass().cast(get(provider.getKey()));
   }
 
@@ -58,7 +58,7 @@
    * <p>Type-safe version of {@link #get(Provider.Key)} for native providers.
    */
   @Nullable
-  default <T extends Info> T get(BuiltinProvider<T> provider) {
+  default <T extends InfoInterface> T get(BuiltinProvider<T> provider) {
     return provider.getValueClass().cast(get(provider.getKey()));
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMap.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMap.java
index 9d83702..29923da 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMap.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMap.java
@@ -14,7 +14,7 @@
 
 package com.google.devtools.build.lib.analysis;
 
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import javax.annotation.Nullable;
@@ -28,7 +28,7 @@
  *
  * <ul>
  *   <li>Declared providers. They are exposed to Skylark and identified by {@link Provider.Key}.
- *       Provider instances are {@link Info}s.
+ *       Provider instances are {@link InfoInterface}s.
  *   <li>Native providers. They are identified by their {@link Class} and their instances are
  *       instances of that class. They should implement {@link TransitiveInfoProvider} marker
  *       interface.
@@ -47,7 +47,7 @@
    * present.
    */
   @Nullable
-  Info getProvider(Provider.Key key);
+  InfoInterface getProvider(Provider.Key key);
 
   /**
    * Returns the instance of a legacy Skylark  with the given name, or {@code null} if not present.
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapBuilder.java
index 7a5ed43..1eb6640 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapBuilder.java
@@ -15,7 +15,7 @@
 package com.google.devtools.build.lib.analysis;
 
 import com.google.common.base.Preconditions;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.Provider;
 import java.util.Arrays;
 import java.util.LinkedHashMap;
@@ -49,7 +49,8 @@
     Preconditions.checkNotNull(providerClass);
     Preconditions.checkNotNull(provider);
     Preconditions.checkState(
-        !(provider instanceof Info), "Expose %s as native declared provider", providerClass);
+        !(provider instanceof InfoInterface), "Expose %s as native declared provider",
+        providerClass);
 
     // TODO(arielb): throw an exception if the providerClass is already present?
     // This is enforced by aspects but RuleConfiguredTarget presents violations
@@ -58,7 +59,7 @@
     return this;
   }
 
-  public TransitiveInfoProviderMapBuilder put(Info classObject) {
+  public TransitiveInfoProviderMapBuilder put(InfoInterface classObject) {
     Preconditions.checkNotNull(classObject);
     Preconditions.checkState(!(classObject instanceof TransitiveInfoProvider),
         "Declared provider %s should not implement TransitiveInfoProvider",
@@ -104,8 +105,8 @@
   }
 
   @Nullable
-  public Info getProvider(Provider.Key key) {
-    return (Info) providers.get(key);
+  public InfoInterface getProvider(Provider.Key key) {
+    return (InfoInterface) providers.get(key);
   }
 
   public TransitiveInfoProviderMap build() {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapImpl.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapImpl.java
index 94edb16..4ae87bf 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapImpl.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoProviderMapImpl.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.collect.ImmutableSharedKeyMap;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
@@ -62,8 +62,8 @@
 
   @Nullable
   @Override
-  public Info getProvider(Provider.Key key) {
-    return (Info) get(key);
+  public InfoInterface getProvider(Provider.Key key) {
+    return (InfoInterface) get(key);
   }
 
   @Nullable
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
index ac626b3..c3d1e80 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
@@ -30,7 +30,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
@@ -184,7 +184,7 @@
   /** Returns a declared provider provided by this target. Only meant to use from Skylark. */
   @Nullable
   @Override
-  public final Info get(Provider.Key providerKey) {
+  public final InfoInterface get(Provider.Key providerKey) {
     if (providerKey.equals(DefaultInfo.PROVIDER.getKey())) {
       return getDefaultProvider();
     }
@@ -193,7 +193,7 @@
 
   /** Implement in subclasses to get a skylark provider for a given {@code providerKey}. */
   @Nullable
-  protected abstract Info rawGetSkylarkProvider(Provider.Key providerKey);
+  protected abstract InfoInterface rawGetSkylarkProvider(Provider.Key providerKey);
 
   public String getRuleClassString() {
     return "";
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/EnvironmentGroupConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/EnvironmentGroupConfiguredTarget.java
index 46dc01c..35bd929 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/EnvironmentGroupConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/EnvironmentGroupConfiguredTarget.java
@@ -17,7 +17,7 @@
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.analysis.TargetContext;
 import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 
@@ -39,7 +39,7 @@
   }
 
   @Override
-  protected Info rawGetSkylarkProvider(Provider.Key providerKey) {
+  protected InfoInterface rawGetSkylarkProvider(Provider.Key providerKey) {
     return null;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java
index cd959fa..309a1a7 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java
@@ -28,7 +28,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 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.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
@@ -90,7 +90,7 @@
   }
 
   @Override
-  protected Info rawGetSkylarkProvider(Provider.Key providerKey) {
+  protected InfoInterface rawGetSkylarkProvider(Provider.Key providerKey) {
     return providers.getProvider(providerKey);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java
index 1e60f64..5a421df 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java
@@ -24,7 +24,7 @@
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.Provider.Key;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
@@ -91,8 +91,8 @@
   }
 
   @Override
-  protected Info rawGetSkylarkProvider(Provider.Key providerKey) {
-    Info provider = providers.getProvider(providerKey);
+  protected InfoInterface rawGetSkylarkProvider(Provider.Key providerKey) {
+    InfoInterface provider = providers.getProvider(providerKey);
     if (provider == null) {
       provider = base.get(providerKey);
     }
@@ -172,7 +172,7 @@
           if (base.get(key) != null || aspectProviders.contains(key)) {
             throw new DuplicateException("Provider " + key + " provided twice");
           }
-          aspectProviders.put((Info) providers.getProviderInstanceAt(i));
+          aspectProviders.put((InfoInterface) providers.getProviderInstanceAt(i));
         }
       }
     }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/PackageGroupConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/PackageGroupConfiguredTarget.java
index bf13586..feef6d4 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/PackageGroupConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/PackageGroupConfiguredTarget.java
@@ -25,7 +25,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.PackageGroup;
 import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents;
 import com.google.devtools.build.lib.packages.Provider;
@@ -103,7 +103,7 @@
   }
 
   @Override
-  protected Info rawGetSkylarkProvider(Provider.Key providerKey) {
+  protected InfoInterface rawGetSkylarkProvider(Provider.Key providerKey) {
     return null;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java
index 2f668f6..2314b43 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java
@@ -37,7 +37,7 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.concurrent.BlazeInterners;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.OutputFile;
 import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents;
 import com.google.devtools.build.lib.packages.Provider;
@@ -211,7 +211,7 @@
   }
 
   @Override
-  protected Info rawGetSkylarkProvider(Provider.Key providerKey) {
+  protected InfoInterface rawGetSkylarkProvider(Provider.Key providerKey) {
     return providers.getProvider(providerKey);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttributesCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttributesCollection.java
index 6ee03b7..d2054db 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttributesCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttributesCollection.java
@@ -22,7 +22,7 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.packages.Attribute;
 import com.google.devtools.build.lib.packages.BuildType;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.packages.StructProvider;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkAttributesCollectionApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
@@ -41,10 +41,10 @@
 /** Information about attributes of a rule an aspect is applied to. */
 class SkylarkAttributesCollection implements SkylarkAttributesCollectionApi {
   private final SkylarkRuleContext skylarkRuleContext;
-  private final Info attrObject;
-  private final Info executableObject;
-  private final Info fileObject;
-  private final Info filesObject;
+  private final StructImpl attrObject;
+  private final StructImpl executableObject;
+  private final StructImpl fileObject;
+  private final StructImpl filesObject;
   private final ImmutableMap<Artifact, FilesToRunProvider> executableRunfilesMap;
   private final String ruleClassName;
 
@@ -85,25 +85,25 @@
   }
 
   @Override
-  public Info getAttr() throws EvalException {
+  public StructImpl getAttr() throws EvalException {
     checkMutable("attr");
     return attrObject;
   }
 
   @Override
-  public Info getExecutable() throws EvalException {
+  public StructImpl getExecutable() throws EvalException {
     checkMutable("executable");
     return executableObject;
   }
 
   @Override
-  public Info getFile() throws EvalException {
+  public StructImpl getFile() throws EvalException {
     checkMutable("file");
     return fileObject;
   }
 
   @Override
-  public Info getFiles() throws EvalException {
+  public StructImpl getFiles() throws EvalException {
     checkMutable("files");
     return filesObject;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
index e0eafd6..73d6b92 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
@@ -34,12 +34,13 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.AdvertisedProviderSet;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.packages.StructProvider;
 import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
@@ -108,7 +109,7 @@
 
       if (ruleContext.hasErrors()) {
         return null;
-      } else if (!(target instanceof Info)
+      } else if (!(target instanceof InfoInterface)
           && target != Runtime.NONE
           && !(target instanceof Iterable)) {
         ruleContext.ruleError(
@@ -217,7 +218,7 @@
   }
 
   private static void addInstrumentedFiles(
-      Info insStruct, RuleContext ruleContext, RuleConfiguredTargetBuilder builder)
+      StructImpl insStruct, RuleContext ruleContext, RuleConfiguredTargetBuilder builder)
       throws EvalException {
     Location insLoc = insStruct.getCreationLoc();
     FileTypeSet fileTypeSet = FileTypeSet.ANY_FILE;
@@ -299,25 +300,26 @@
       SkylarkRuleContext context, RuleConfiguredTargetBuilder builder, Object target, Location loc)
       throws EvalException {
 
-    Info oldStyleProviders = StructProvider.STRUCT.createEmpty(loc);
-    Map<Provider.Key, Info> declaredProviders = new LinkedHashMap<>();
+    StructImpl oldStyleProviders = StructProvider.STRUCT.createEmpty(loc);
+    Map<Provider.Key, InfoInterface> declaredProviders = new LinkedHashMap<>();
 
-    if (target instanceof Info) {
+    if (target instanceof InfoInterface) {
       // Either an old-style struct or a single declared provider (not in a list)
-      Info struct = (Info) target;
+      InfoInterface info = (InfoInterface) target;
       // Use the creation location of this struct as a better reference in error messages
-      loc = struct.getCreationLoc();
-      if (struct.getProvider().getKey().equals(StructProvider.STRUCT.getKey())) {
+      loc = info.getCreationLoc();
+      if (info.getProvider().getKey().equals(StructProvider.STRUCT.getKey())) {
         // Old-style struct, but it may contain declared providers
+        StructImpl struct = (StructImpl) target;
         oldStyleProviders = struct;
 
         if (struct.hasField("providers")) {
           Iterable iterable = cast("providers", struct, Iterable.class, loc);
           for (Object o : iterable) {
-            Info declaredProvider =
+            InfoInterface declaredProvider =
                 SkylarkType.cast(
                     o,
-                    Info.class,
+                    InfoInterface.class,
                     loc,
                     "The value of 'providers' should be a sequence of declared providers");
             Provider.Key providerKey = declaredProvider.getProvider().getKey();
@@ -330,17 +332,17 @@
           }
         }
       } else {
-        Provider.Key providerKey = struct.getProvider().getKey();
+        Provider.Key providerKey = info.getProvider().getKey();
         // Single declared provider
-        declaredProviders.put(providerKey, struct);
+        declaredProviders.put(providerKey, info);
       }
     } else if (target instanceof Iterable) {
       // Sequence of declared providers
       for (Object o : (Iterable) target) {
-        Info declaredProvider =
+        InfoInterface declaredProvider =
             SkylarkType.cast(
                 o,
-                Info.class,
+                InfoInterface.class,
                 loc,
                 "A return value of a rule implementation function should be "
                     + "a sequence of declared providers");
@@ -356,12 +358,12 @@
 
     boolean defaultProviderProvidedExplicitly = false;
 
-    for (Info declaredProvider : declaredProviders.values()) {
+    for (InfoInterface declaredProvider : declaredProviders.values()) {
       if (declaredProvider
           .getProvider()
           .getKey()
           .equals(DefaultInfo.PROVIDER.getKey())) {
-        parseDefaultProviderFields(declaredProvider, context, builder);
+        parseDefaultProviderFields((DefaultInfo) declaredProvider, context, builder);
         defaultProviderProvidedExplicitly = true;
       } else {
         builder.addSkylarkDeclaredProvider(declaredProvider);
@@ -387,10 +389,10 @@
       } else if (field.equals("output_groups")) {
         addOutputGroups(oldStyleProviders.getValue(field), loc, builder);
       } else if (field.equals("instrumented_files")) {
-        Info insStruct = cast("instrumented_files", oldStyleProviders, Info.class, loc);
+        StructImpl insStruct = cast("instrumented_files", oldStyleProviders, StructImpl.class, loc);
         addInstrumentedFiles(insStruct, context.getRuleContext(), builder);
       } else if (isNativeDeclaredProviderWithLegacySkylarkName(oldStyleProviders.getValue(field))) {
-        builder.addNativeDeclaredProvider((Info) oldStyleProviders.getValue(field));
+        builder.addNativeDeclaredProvider((InfoInterface) oldStyleProviders.getValue(field));
       } else if (!field.equals("providers")) {
         // We handled providers already.
         builder.addSkylarkTransitiveInfo(field, oldStyleProviders.getValue(field), loc);
@@ -399,10 +401,10 @@
   }
 
   private static boolean isNativeDeclaredProviderWithLegacySkylarkName(Object value) {
-    if (!(value instanceof Info)) {
+    if (!(value instanceof InfoInterface)) {
       return false;
     }
-    return ((Info) value).getProvider() instanceof NativeProvider.WithLegacySkylarkName;
+    return ((InfoInterface) value).getProvider() instanceof NativeProvider.WithLegacySkylarkName;
   }
 
   /**
@@ -410,7 +412,7 @@
    * throws an {@link EvalException} if there are unknown fields.
    */
   private static void parseDefaultProviderFields(
-      Info provider, SkylarkRuleContext context, RuleConfiguredTargetBuilder builder)
+      StructImpl provider, SkylarkRuleContext context, RuleConfiguredTargetBuilder builder)
       throws EvalException {
     SkylarkNestedSet files = null;
     Runfiles statelessRunfiles = null;
@@ -557,7 +559,7 @@
     }
 
     if (ruleContext.getRule().getRuleClassObject().isSkylarkTestable()) {
-      Info actions =
+      InfoInterface actions =
           ActionsProvider.create(ruleContext.getAnalysisEnvironment().getRegisteredActions());
       builder.addSkylarkDeclaredProvider(actions);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
index 6fda353..36b16a0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
@@ -56,11 +56,11 @@
 import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunction;
-import com.google.devtools.build.lib.packages.Info;
 import com.google.devtools.build.lib.packages.OutputFile;
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.packages.RawAttributeMapper;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.packages.StructProvider;
 import com.google.devtools.build.lib.shell.ShellUtils;
 import com.google.devtools.build.lib.shell.ShellUtils.TokenizationException;
@@ -137,7 +137,7 @@
   private SkylarkDict<String, String> makeVariables;
   private SkylarkAttributesCollection attributesCollection;
   private SkylarkAttributesCollection ruleAttributesCollection;
-  private Info splitAttributes;
+  private StructImpl splitAttributes;
 
   // TODO(bazel-team): we only need this because of the css_binary rule.
   private ImmutableMap<Artifact, Label> artifactsLabelMap;
@@ -413,7 +413,7 @@
     return ruleLabelCanonicalName;
   }
 
-  private static Info buildSplitAttributeInfo(
+  private static StructImpl buildSplitAttributeInfo(
       Collection<Attribute> attributes, RuleContext ruleContext) {
 
     ImmutableMap.Builder<String, Object> splitAttrInfos = ImmutableMap.builder();
@@ -502,13 +502,13 @@
   }
 
   @Override
-  public Info getAttr() throws EvalException {
+  public StructImpl getAttr() throws EvalException {
     checkMutable("attr");
     return attributesCollection.getAttr();
   }
 
   @Override
-  public Info getSplitAttr() throws EvalException {
+  public StructImpl getSplitAttr() throws EvalException {
     checkMutable("split_attr");
     if (splitAttributes == null) {
       throw new EvalException(
@@ -519,21 +519,21 @@
 
   /** See {@link RuleContext#getExecutablePrerequisite(String, Mode)}. */
   @Override
-  public Info getExecutable() throws EvalException {
+  public StructImpl getExecutable() throws EvalException {
     checkMutable("executable");
     return attributesCollection.getExecutable();
   }
 
   /** See {@link RuleContext#getPrerequisiteArtifact(String, Mode)}. */
   @Override
-  public Info getFile() throws EvalException {
+  public StructImpl getFile() throws EvalException {
     checkMutable("file");
     return attributesCollection.getFile();
   }
 
   /** See {@link RuleContext#getPrerequisiteArtifacts(String, Mode)}. */
   @Override
-  public Info getFiles() throws EvalException {
+  public StructImpl getFiles() throws EvalException {
     checkMutable("files");
     return attributesCollection.getFiles();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryResolvedEvent.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryResolvedEvent.java
index a3218e1..5c608f7 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryResolvedEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryResolvedEvent.java
@@ -24,8 +24,8 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.events.ExtendedEventHandler.ProgressLike;
 import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.Info;
 import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.Runtime;
 import com.google.devtools.build.lib.vfs.Path;
@@ -51,7 +51,7 @@
    */
   private final Object resolvedInformation;
 
-  public RepositoryResolvedEvent(Rule rule, Info attrs, Path outputDirectory, Object result) {
+  public RepositoryResolvedEvent(Rule rule, StructImpl attrs, Path outputDirectory, Object result) {
     ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
 
     String originalClass =
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
index d52388c..a618eae 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
@@ -28,8 +28,8 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.Info;
 import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.packages.StructProvider;
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException;
@@ -67,7 +67,7 @@
 
   private final Rule rule;
   private final Path outputDirectory;
-  private final Info attrObject;
+  private final StructImpl attrObject;
   private final SkylarkOS osObject;
   private final Environment env;
   private final HttpDownloader httpDownloader;
@@ -109,7 +109,7 @@
   }
 
   @Override
-  public Info getAttr() {
+  public StructImpl getAttr() {
     return attrObject;
   }
 
@@ -486,7 +486,7 @@
    * potentially more expensive operations.
    */
   public void enforceLabelAttributes() throws EvalException, InterruptedException {
-    Info attr = getAttr();
+    StructImpl attr = getAttr();
     for (String name : attr.getFieldNames()) {
       Object value = attr.getValue(name);
       if (value instanceof Label) {
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BuiltinProvider.java b/src/main/java/com/google/devtools/build/lib/packages/BuiltinProvider.java
index 039f1ef..5e1e823 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/BuiltinProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/BuiltinProvider.java
@@ -32,7 +32,7 @@
  * true, and with {@link SkylarkConstructor} for the info type it constructs.
  */
 @Immutable
-public abstract class BuiltinProvider<T extends Info> implements Provider {
+public abstract class BuiltinProvider<T extends InfoInterface> implements Provider {
   private final NativeKey key;
   private final String name;
   private final Class<T> valueClass;
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Info.java b/src/main/java/com/google/devtools/build/lib/packages/Info.java
index 4bc863b..8b6bd15 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Info.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Info.java
@@ -13,77 +13,39 @@
 // limitations under the License.
 package com.google.devtools.build.lib.packages;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Ordering;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
-import com.google.devtools.build.lib.skylarkbuildapi.StructApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
-import com.google.devtools.build.lib.syntax.ClassObject;
-import com.google.devtools.build.lib.syntax.Environment;
-import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.EvalUtils;
-import com.google.devtools.build.lib.syntax.Printer;
-import com.google.devtools.build.lib.syntax.Runtime;
-import com.google.devtools.build.lib.syntax.SkylarkDict;
-import com.google.devtools.build.lib.syntax.SkylarkList;
-import com.google.devtools.build.lib.syntax.SkylarkType;
-import com.google.protobuf.TextFormat;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
 import javax.annotation.Nullable;
 
-/** An instance (in the Skylark sense, not Java) of a {@link Provider}. */
-public abstract class Info implements ClassObject, StructApi, Serializable {
+/**
+ * Generic implementation of {@link InfoInterface}.
+ *
+ * <p>Natively-defined Info objects should subclass this to be registered as Info objects that may
+ * be passed between targets.
+ */
+public abstract class Info implements Serializable, InfoInterface, SkylarkValue {
 
   /** The {@link Provider} that describes the type of this instance. */
-  private final Provider provider;
+  protected final Provider provider;
 
   /**
    * The Skylark location where this provider instance was created.
    *
    * <p>Built-in provider instances may use {@link Location#BUILTIN}.
    */
-  @VisibleForSerialization protected final Location location;
+  @VisibleForSerialization
+  protected final Location location;
 
-  /**
-   * Constructs an {@link Info}.
-   *
-   * @param provider the provider describing the type of this instance
-   * @param location the Skylark location where this instance is created. If null, defaults to
-   *     {@link Location#BUILTIN}.
-   */
   protected Info(Provider provider, @Nullable Location location) {
     this.provider = Preconditions.checkNotNull(provider);
     this.location = location == null ? Location.BUILTIN : location;
   }
 
   /**
-   * Preprocesses a map of field values to convert the field names and field values to
-   * Skylark-acceptable names and types.
-   *
-   * <p>This preserves the order of the map entries.
-   */
-  protected static ImmutableMap<String, Object> copyValues(Map<String, Object> values) {
-    Preconditions.checkNotNull(values);
-    ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
-    for (Map.Entry<String, Object> e : values.entrySet()) {
-      builder.put(
-          Attribute.getSkylarkName(e.getKey()),
-          SkylarkType.convertToSkylark(e.getValue(), (Environment) null));
-    }
-    return builder.build();
-  }
-
-  /**
    * Returns the Skylark location where this provider instance was created.
    *
    * <p>Builtin provider instances may return {@link Location#BUILTIN}.
@@ -96,284 +58,10 @@
     return provider;
   }
 
-  /**
-   * Returns whether the given field name exists.
-   *
-   * <p>This conceptually extends the API for {@link ClassObject}.
-   */
-  public abstract boolean hasField(String name);
-
-  /**
-   * <p>Wraps {@link ClassObject#getValue(String)}, returning null in cases where
-   * {@link EvalException} would have been thrown.
-   */
-  @VisibleForTesting
-  public Object getValueOrNull(String name) {
-    try {
-      return getValue(name);
-    } catch (EvalException e) {
-      return null;
-    }
-  }
-
-  /**
-   * Returns the result of {@link #getValue(String)}, cast as the given type, throwing {@link
-   * EvalException} if the cast fails.
-   */
-  public <T> T getValue(String key, Class<T> type) throws EvalException {
-    Object obj = getValue(key);
-    if (obj == null) {
-      return null;
-    }
-    SkylarkType.checkType(obj, type, key);
-    return type.cast(obj);
-  }
-
-  /**
-   * {@inheritDoc}
-   *
-   * <p>Overrides {@link ClassObject#getFieldNames()}, but does not allow {@link EvalException} to
-   * be thrown.
-   */
-  @Override
-  public abstract ImmutableCollection<String> getFieldNames();
-
-  /**
-   * Returns the error message format to use for unknown fields.
-   *
-   * <p>By default, it is the one specified by the provider.
-   */
-  protected String getErrorMessageFormatForUnknownField() {
-    return provider.getErrorMessageFormatForUnknownField();
-  }
-
-  @Override
-  public String getErrorMessageForUnknownField(String name) {
-    String suffix = "Available attributes: "
-        + Joiner.on(", ").join(Ordering.natural().sortedCopy(getFieldNames()));
-    return String.format(getErrorMessageFormatForUnknownField(), name) + "\n" + suffix;
-  }
-
-  @Override
-  public boolean equals(Object otherObject) {
-    if (!(otherObject instanceof Info)) {
-      return false;
-    }
-    Info other = (Info) otherObject;
-    if (this == other) {
-      return true;
-    }
-    if (!this.provider.equals(other.provider)) {
-      return false;
-    }
-    // Compare objects' fields and their values
-    if (!this.getFieldNames().equals(other.getFieldNames())) {
-      return false;
-    }
-    for (String field : getFieldNames()) {
-      if (!Objects.equal(this.getValueOrNull(field), other.getValueOrNull(field))) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    List<String> fields = new ArrayList<>(getFieldNames());
-    Collections.sort(fields);
-    List<Object> objectsToHash = new ArrayList<>();
-    objectsToHash.add(provider);
-    for (String field : fields) {
-      objectsToHash.add(field);
-      objectsToHash.add(getValueOrNull(field));
-    }
-    return Objects.hashCode(objectsToHash.toArray());
-  }
-
-  /**
-   * Convert the object to string using Skylark syntax. The output tries to be reversible (but there
-   * is no guarantee, it depends on the actual values).
-   */
   @Override
   public void repr(SkylarkPrinter printer) {
-    boolean first = true;
-    printer.append("struct(");
-    // Sort by key to ensure deterministic output.
-    for (String fieldName : Ordering.natural().sortedCopy(getFieldNames())) {
-      if (!first) {
-        printer.append(", ");
-      }
-      first = false;
-      printer.append(fieldName);
-      printer.append(" = ");
-      printer.repr(getValueOrNull(fieldName));
-    }
-    printer.append(")");
-  }
-
-  @Override
-  public String toProto(Location loc) throws EvalException {
-    StringBuilder sb = new StringBuilder();
-    printProtoTextMessage(this, sb, 0, loc);
-    return sb.toString();
-  }
-
-  private void printProtoTextMessage(ClassObject object, StringBuilder sb, int indent, Location loc)
-      throws EvalException {
-    // For determinism sort the fields alphabetically.
-    List<String> fields = new ArrayList<>(object.getFieldNames());
-    Collections.sort(fields);
-    for (String field : fields) {
-      printProtoTextMessage(field, object.getValue(field), sb, indent, loc);
-    }
-  }
-
-  private void printProtoTextMessage(
-      String key, Object value, StringBuilder sb, int indent, Location loc, String container)
-      throws EvalException {
-    if (value instanceof ClassObject) {
-      print(sb, key + " {", indent);
-      printProtoTextMessage((ClassObject) value, sb, indent + 1, loc);
-      print(sb, "}", indent);
-    } else if (value instanceof String) {
-      print(
-          sb,
-          key + ": \"" + escapeDoubleQuotesAndBackslashesAndNewlines((String) value) + "\"",
-          indent);
-    } else if (value instanceof Integer) {
-      print(sb, key + ": " + value, indent);
-    } else if (value instanceof Boolean) {
-      // We're relying on the fact that Java converts Booleans to Strings in the same way
-      // as the protocol buffers do.
-      print(sb, key + ": " + value, indent);
-    } else {
-      throw new EvalException(
-          loc,
-          "Invalid text format, expected a struct, a string, a bool, or an int but got a "
-              + EvalUtils.getDataTypeName(value)
-              + " for "
-              + container
-              + " '"
-              + key
-              + "'");
-    }
-  }
-
-  private void printProtoTextMessage(
-      String key, Object value, StringBuilder sb, int indent, Location loc) throws EvalException {
-    if (value instanceof SkylarkList) {
-      for (Object item : ((SkylarkList) value)) {
-        // TODO(bazel-team): There should be some constraint on the fields of the structs
-        // in the same list but we ignore that for now.
-        printProtoTextMessage(key, item, sb, indent, loc, "list element in struct field");
-      }
-    } else {
-      printProtoTextMessage(key, value, sb, indent, loc, "struct field");
-    }
-  }
-
-  private void print(StringBuilder sb, String text, int indent) {
-    for (int i = 0; i < indent; i++) {
-      sb.append("  ");
-    }
-    sb.append(text);
-    sb.append("\n");
-  }
-
-  /**
-   * Escapes the given string for use in proto/JSON string.
-   *
-   * <p>This escapes double quotes, backslashes, and newlines.
-   */
-  private static String escapeDoubleQuotesAndBackslashesAndNewlines(String string) {
-    return TextFormat.escapeDoubleQuotesAndBackslashes(string).replace("\n", "\\n");
-  }
-
-  @Override
-  public String toJson(Location loc) throws EvalException {
-    StringBuilder sb = new StringBuilder();
-    printJson(this, sb, loc, "struct field", null);
-    return sb.toString();
-  }
-
-  private void printJson(Object value, StringBuilder sb, Location loc, String container, String key)
-      throws EvalException {
-    if (value == Runtime.NONE) {
-      sb.append("null");
-    } else if (value instanceof ClassObject) {
-      sb.append("{");
-
-      String join = "";
-      for (String field : ((ClassObject) value).getFieldNames()) {
-        sb.append(join);
-        join = ",";
-        sb.append("\"");
-        sb.append(field);
-        sb.append("\":");
-        printJson(((ClassObject) value).getValue(field), sb, loc, "struct field", field);
-      }
-      sb.append("}");
-    } else if (value instanceof SkylarkDict) {
-      sb.append("{");
-      String join = "";
-      for (Map.Entry<?, ?> entry : ((SkylarkDict<?, ?>) value).entrySet()) {
-        sb.append(join);
-        join = ",";
-        if (!(entry.getKey() instanceof String)) {
-          String errorMessage =
-              "Keys must be a string but got a "
-                  + EvalUtils.getDataTypeName(entry.getKey())
-                  + " for "
-                  + container;
-          if (key != null) {
-            errorMessage += " '" + key + "'";
-          }
-          throw new EvalException(loc, errorMessage);
-        }
-        sb.append("\"");
-        sb.append(entry.getKey());
-        sb.append("\":");
-        printJson(entry.getValue(), sb, loc, "dict value", String.valueOf(entry.getKey()));
-      }
-      sb.append("}");
-    } else if (value instanceof List) {
-      sb.append("[");
-      String join = "";
-      for (Object item : ((List) value)) {
-        sb.append(join);
-        join = ",";
-        printJson(item, sb, loc, "list element in struct field", key);
-      }
-      sb.append("]");
-    } else if (value instanceof String) {
-      sb.append("\"");
-      sb.append(jsonEscapeString((String) value));
-      sb.append("\"");
-    } else if (value instanceof Integer || value instanceof Boolean) {
-      sb.append(value);
-    } else {
-      String errorMessage =
-          "Invalid text format, expected a struct, a string, a bool, or an int "
-              + "but got a "
-              + EvalUtils.getDataTypeName(value)
-              + " for "
-              + container;
-      if (key != null) {
-        errorMessage += " '" + key + "'";
-      }
-      throw new EvalException(loc, errorMessage);
-    }
-  }
-
-  private String jsonEscapeString(String string) {
-    return escapeDoubleQuotesAndBackslashesAndNewlines(string)
-        .replace("\r", "\\r")
-        .replace("\t", "\\t");
-  }
-
-  @Override
-  public String toString() {
-    return Printer.repr(this);
+    printer.append("<instance of provider ");
+    printer.append(provider.getPrintableName());
+    printer.append(">");
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/InfoInterface.java b/src/main/java/com/google/devtools/build/lib/packages/InfoInterface.java
new file mode 100644
index 0000000..b2a24e0
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/InfoInterface.java
@@ -0,0 +1,38 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import com.google.devtools.build.lib.events.Location;
+
+/**
+ * An instance (in the Skylark sense, not Java) of a {@link Provider}.
+ *
+ * <p>Info objects are specially handled in skylark, serving as units of information passed
+ * between targets. Each Info object must be associated with a Provider key, defined by the
+ * Provider which constructs Info objects of its type.
+ */
+public interface InfoInterface {
+
+  /**
+   * Returns the Skylark location where this instance was created.
+   *
+   * <p>Builtin provider instances may return {@link Location#BUILTIN}.
+   */
+  Location getCreationLoc();
+
+  /**
+   * Returns the provider instance that constructs instances of this info.
+   */
+  Provider getProvider();
+}
\ No newline at end of file
diff --git a/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java b/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java
index 2652671..5c18976 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java
@@ -22,9 +22,9 @@
 import com.google.devtools.build.lib.syntax.MethodDescriptor;
 import java.util.Map;
 
-/** Base class for native implementations of {@link Info}. */
+/** Base class for native implementations of {@link StructImpl}. */
 // todo(vladmos,dslomov): make abstract once DefaultInfo stops instantiating it.
-public class NativeInfo extends Info {
+public class NativeInfo extends StructImpl {
   protected final ImmutableMap<String, Object> values;
 
   // Initialized lazily.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java b/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java
index c59d81e..12f93f24 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java
@@ -44,7 +44,7 @@
  */
 @Immutable
 @Deprecated
-public abstract class NativeProvider<V extends Info> extends ProviderFromFunction {
+public abstract class NativeProvider<V extends InfoInterface> extends ProviderFromFunction {
   private final NativeKey key;
   private final String errorMessageFormatForUnknownField;
 
@@ -128,7 +128,7 @@
   }
 
   @Override
-  protected Info createInstanceFromSkylark(Object[] args, Environment env, Location loc)
+  protected InfoInterface createInstanceFromSkylark(Object[] args, Environment env, Location loc)
       throws EvalException {
     throw new EvalException(
         loc, String.format("'%s' cannot be constructed from Skylark", getPrintableName()));
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Provider.java b/src/main/java/com/google/devtools/build/lib/packages/Provider.java
index 895240a..2ef9cca 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Provider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Provider.java
@@ -19,7 +19,7 @@
 import com.google.devtools.build.lib.syntax.ClassObject;
 
 /**
- * Declared Provider (a constructor for {@link Info}).
+ * Declared Provider (a constructor for {@link InfoInterface}).
  *
  * <p>Declared providers can be declared either natively ({@link NativeProvider} or in Skylark
  * {@link SkylarkProvider}.
@@ -27,7 +27,7 @@
  * <p>{@link Provider} serves both as "type identifier" for declared provider instances and as a
  * function that can be called to construct a provider. To the Skylark user, there are "providers"
  * and "provider instances"; the former is a Java instance of this class, and the latter is a Java
- * instance of {@link Info}.
+ * instance of {@link InfoInterface}.
  *
  * <p>Prefer to use {@link Key} as a serializable identifier of {@link Provider}. In particular,
  * {@link Key} should be used in all data structures exposed to Skyframe.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ProviderFromFunction.java b/src/main/java/com/google/devtools/build/lib/packages/ProviderFromFunction.java
index 134ed49..a293393 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/ProviderFromFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/ProviderFromFunction.java
@@ -66,6 +66,6 @@
    *
    * @param args an array of argument values sorted as per the signature ({@see BaseFunction#call})
    */
-  protected abstract Info createInstanceFromSkylark(Object[] args, Environment env, Location loc)
-      throws EvalException;
+  protected abstract InfoInterface createInstanceFromSkylark(
+      Object[] args, Environment env, Location loc) throws EvalException;
 }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java
index 1b5795c..62eb6d0 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java
@@ -43,7 +43,7 @@
  * implementing classes). Schemaless instances are map-based, while schemaful instances have a fixed
  * layout and array and are therefore more efficient.
  */
-public abstract class SkylarkInfo extends Info implements Concatable, SkylarkClassObject {
+public abstract class SkylarkInfo extends StructImpl implements Concatable, SkylarkClassObject {
 
   // Private because this should not be subclassed outside this file.
   private SkylarkInfo(Provider provider, @Nullable Location loc) {
@@ -124,9 +124,9 @@
    * NativeInfo} subclass.
    */
   // TODO(bazel-team): Make the special structs that need a custom error message use a different
-  // provider (subclassing NativeProvider) and a different Info implementation. Then remove this
-  // functionality, thereby saving a string pointer field for the majority of providers that don't
-  // need it.
+  // provider (subclassing NativeProvider) and a different StructImpl implementation. Then remove
+  // this functionality, thereby saving a string pointer field for the majority of providers that
+  // don't need it.
   public static SkylarkInfo createSchemalessWithCustomMessage(
       Provider provider, Map<String, Object> values, String errorMessageFormatForUnknownField) {
     Preconditions.checkNotNull(errorMessageFormatForUnknownField);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StructImpl.java b/src/main/java/com/google/devtools/build/lib/packages/StructImpl.java
new file mode 100644
index 0000000..b14be5a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/StructImpl.java
@@ -0,0 +1,357 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Ordering;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.skylarkbuildapi.StructApi;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.syntax.ClassObject;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.EvalUtils;
+import com.google.devtools.build.lib.syntax.Printer;
+import com.google.devtools.build.lib.syntax.Runtime;
+import com.google.devtools.build.lib.syntax.SkylarkDict;
+import com.google.devtools.build.lib.syntax.SkylarkList;
+import com.google.devtools.build.lib.syntax.SkylarkType;
+import com.google.protobuf.TextFormat;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/**
+ * A generic skylark object with fields, constructable by calling {@code struct()} in skylark.
+ */
+public abstract class StructImpl extends Info
+    implements ClassObject, StructApi, Serializable {
+
+  /**
+   * Constructs an {@link StructImpl}.
+   *
+   * @param provider the provider describing the type of this instance
+   * @param location the Skylark location where this instance is created. If null, defaults to
+   *     {@link Location#BUILTIN}.
+   */
+  protected StructImpl(Provider provider, @Nullable Location location) {
+    super(provider, location);
+  }
+
+  /**
+   * Preprocesses a map of field values to convert the field names and field values to
+   * Skylark-acceptable names and types.
+   *
+   * <p>This preserves the order of the map entries.
+   */
+  protected static ImmutableMap<String, Object> copyValues(Map<String, Object> values) {
+    Preconditions.checkNotNull(values);
+    ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
+    for (Map.Entry<String, Object> e : values.entrySet()) {
+      builder.put(
+          Attribute.getSkylarkName(e.getKey()),
+          SkylarkType.convertToSkylark(e.getValue(), (Environment) null));
+    }
+    return builder.build();
+  }
+
+  /**
+   * Returns whether the given field name exists.
+   *
+   * <p>This conceptually extends the API for {@link ClassObject}.
+   */
+  public abstract boolean hasField(String name);
+
+  /**
+   * <p>Wraps {@link ClassObject#getValue(String)}, returning null in cases where
+   * {@link EvalException} would have been thrown.
+   */
+  @VisibleForTesting
+  public Object getValueOrNull(String name) {
+    try {
+      return getValue(name);
+    } catch (EvalException e) {
+      return null;
+    }
+  }
+
+  /**
+   * Returns the result of {@link #getValue(String)}, cast as the given type, throwing {@link
+   * EvalException} if the cast fails.
+   */
+  public <T> T getValue(String key, Class<T> type) throws EvalException {
+    Object obj = getValue(key);
+    if (obj == null) {
+      return null;
+    }
+    SkylarkType.checkType(obj, type, key);
+    return type.cast(obj);
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * <p>Overrides {@link ClassObject#getFieldNames()}, but does not allow {@link EvalException} to
+   * be thrown.
+   */
+  @Override
+  public abstract ImmutableCollection<String> getFieldNames();
+
+  /**
+   * Returns the error message format to use for unknown fields.
+   *
+   * <p>By default, it is the one specified by the provider.
+   */
+  protected String getErrorMessageFormatForUnknownField() {
+    return provider.getErrorMessageFormatForUnknownField();
+  }
+
+  @Override
+  public String getErrorMessageForUnknownField(String name) {
+    String suffix = "Available attributes: "
+        + Joiner.on(", ").join(Ordering.natural().sortedCopy(getFieldNames()));
+    return String.format(getErrorMessageFormatForUnknownField(), name) + "\n" + suffix;
+  }
+
+  @Override
+  public boolean equals(Object otherObject) {
+    if (!(otherObject instanceof StructImpl)) {
+      return false;
+    }
+    StructImpl other = (StructImpl) otherObject;
+    if (this == other) {
+      return true;
+    }
+    if (!this.provider.equals(other.provider)) {
+      return false;
+    }
+    // Compare objects' fields and their values
+    if (!this.getFieldNames().equals(other.getFieldNames())) {
+      return false;
+    }
+    for (String field : getFieldNames()) {
+      if (!Objects.equal(this.getValueOrNull(field), other.getValueOrNull(field))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    List<String> fields = new ArrayList<>(getFieldNames());
+    Collections.sort(fields);
+    List<Object> objectsToHash = new ArrayList<>();
+    objectsToHash.add(provider);
+    for (String field : fields) {
+      objectsToHash.add(field);
+      objectsToHash.add(getValueOrNull(field));
+    }
+    return Objects.hashCode(objectsToHash.toArray());
+  }
+
+  /**
+   * Convert the object to string using Skylark syntax. The output tries to be reversible (but there
+   * is no guarantee, it depends on the actual values).
+   */
+  @Override
+  public void repr(SkylarkPrinter printer) {
+    boolean first = true;
+    printer.append("struct(");
+    // Sort by key to ensure deterministic output.
+    for (String fieldName : Ordering.natural().sortedCopy(getFieldNames())) {
+      if (!first) {
+        printer.append(", ");
+      }
+      first = false;
+      printer.append(fieldName);
+      printer.append(" = ");
+      printer.repr(getValueOrNull(fieldName));
+    }
+    printer.append(")");
+  }
+
+  @Override
+  public String toProto(Location loc) throws EvalException {
+    StringBuilder sb = new StringBuilder();
+    printProtoTextMessage(this, sb, 0, loc);
+    return sb.toString();
+  }
+
+  private void printProtoTextMessage(ClassObject object, StringBuilder sb, int indent, Location loc)
+      throws EvalException {
+    // For determinism sort the fields alphabetically.
+    List<String> fields = new ArrayList<>(object.getFieldNames());
+    Collections.sort(fields);
+    for (String field : fields) {
+      printProtoTextMessage(field, object.getValue(field), sb, indent, loc);
+    }
+  }
+
+  private void printProtoTextMessage(
+      String key, Object value, StringBuilder sb, int indent, Location loc, String container)
+      throws EvalException {
+    if (value instanceof ClassObject) {
+      print(sb, key + " {", indent);
+      printProtoTextMessage((ClassObject) value, sb, indent + 1, loc);
+      print(sb, "}", indent);
+    } else if (value instanceof String) {
+      print(
+          sb,
+          key + ": \"" + escapeDoubleQuotesAndBackslashesAndNewlines((String) value) + "\"",
+          indent);
+    } else if (value instanceof Integer) {
+      print(sb, key + ": " + value, indent);
+    } else if (value instanceof Boolean) {
+      // We're relying on the fact that Java converts Booleans to Strings in the same way
+      // as the protocol buffers do.
+      print(sb, key + ": " + value, indent);
+    } else {
+      throw new EvalException(
+          loc,
+          "Invalid text format, expected a struct, a string, a bool, or an int but got a "
+              + EvalUtils.getDataTypeName(value)
+              + " for "
+              + container
+              + " '"
+              + key
+              + "'");
+    }
+  }
+
+  private void printProtoTextMessage(
+      String key, Object value, StringBuilder sb, int indent, Location loc) throws EvalException {
+    if (value instanceof SkylarkList) {
+      for (Object item : ((SkylarkList) value)) {
+        // TODO(bazel-team): There should be some constraint on the fields of the structs
+        // in the same list but we ignore that for now.
+        printProtoTextMessage(key, item, sb, indent, loc, "list element in struct field");
+      }
+    } else {
+      printProtoTextMessage(key, value, sb, indent, loc, "struct field");
+    }
+  }
+
+  private void print(StringBuilder sb, String text, int indent) {
+    for (int i = 0; i < indent; i++) {
+      sb.append("  ");
+    }
+    sb.append(text);
+    sb.append("\n");
+  }
+
+  /**
+   * Escapes the given string for use in proto/JSON string.
+   *
+   * <p>This escapes double quotes, backslashes, and newlines.
+   */
+  private static String escapeDoubleQuotesAndBackslashesAndNewlines(String string) {
+    return TextFormat.escapeDoubleQuotesAndBackslashes(string).replace("\n", "\\n");
+  }
+
+  @Override
+  public String toJson(Location loc) throws EvalException {
+    StringBuilder sb = new StringBuilder();
+    printJson(this, sb, loc, "struct field", null);
+    return sb.toString();
+  }
+
+  private void printJson(Object value, StringBuilder sb, Location loc, String container, String key)
+      throws EvalException {
+    if (value == Runtime.NONE) {
+      sb.append("null");
+    } else if (value instanceof ClassObject) {
+      sb.append("{");
+
+      String join = "";
+      for (String field : ((ClassObject) value).getFieldNames()) {
+        sb.append(join);
+        join = ",";
+        sb.append("\"");
+        sb.append(field);
+        sb.append("\":");
+        printJson(((ClassObject) value).getValue(field), sb, loc, "struct field", field);
+      }
+      sb.append("}");
+    } else if (value instanceof SkylarkDict) {
+      sb.append("{");
+      String join = "";
+      for (Map.Entry<?, ?> entry : ((SkylarkDict<?, ?>) value).entrySet()) {
+        sb.append(join);
+        join = ",";
+        if (!(entry.getKey() instanceof String)) {
+          String errorMessage =
+              "Keys must be a string but got a "
+                  + EvalUtils.getDataTypeName(entry.getKey())
+                  + " for "
+                  + container;
+          if (key != null) {
+            errorMessage += " '" + key + "'";
+          }
+          throw new EvalException(loc, errorMessage);
+        }
+        sb.append("\"");
+        sb.append(entry.getKey());
+        sb.append("\":");
+        printJson(entry.getValue(), sb, loc, "dict value", String.valueOf(entry.getKey()));
+      }
+      sb.append("}");
+    } else if (value instanceof List) {
+      sb.append("[");
+      String join = "";
+      for (Object item : ((List) value)) {
+        sb.append(join);
+        join = ",";
+        printJson(item, sb, loc, "list element in struct field", key);
+      }
+      sb.append("]");
+    } else if (value instanceof String) {
+      sb.append("\"");
+      sb.append(jsonEscapeString((String) value));
+      sb.append("\"");
+    } else if (value instanceof Integer || value instanceof Boolean) {
+      sb.append(value);
+    } else {
+      String errorMessage =
+          "Invalid text format, expected a struct, a string, a bool, or an int "
+              + "but got a "
+              + EvalUtils.getDataTypeName(value)
+              + " for "
+              + container;
+      if (key != null) {
+        errorMessage += " '" + key + "'";
+      }
+      throw new EvalException(loc, errorMessage);
+    }
+  }
+
+  private String jsonEscapeString(String string) {
+    return escapeDoubleQuotesAndBackslashesAndNewlines(string)
+        .replace("\r", "\\r")
+        .replace("\t", "\\t");
+  }
+
+  @Override
+  public String toString() {
+    return Printer.repr(this);
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java b/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java
index 7ac0d3a..12484ea 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StructProvider.java
@@ -26,18 +26,18 @@
  *
  * <p>Its singleton instance is {@link StructProvider#STRUCT}.
  */
-public final class StructProvider extends BuiltinProvider<Info>
+public final class StructProvider extends BuiltinProvider<StructImpl>
     implements StructApi.StructProviderApi {
 
   /** "struct" function. */
   public static final StructProvider STRUCT = new StructProvider();
 
   StructProvider() {
-    super("struct", Info.class);
+    super("struct", StructImpl.class);
   }
 
   @Override
-  public Info createStruct(SkylarkDict<?, ?> kwargs, Location loc) throws EvalException {
+  public StructImpl createStruct(SkylarkDict<?, ?> kwargs, Location loc) throws EvalException {
     Map<String, Object> kwargsMap = kwargs.getContents(String.class, Object.class, "kwargs");
     if (kwargsMap.containsKey("to_json")) {
       throw new EvalException(loc, "cannot override built-in struct function 'to_json'");
diff --git a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
index a0e9d7a..a21a5b9 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
@@ -24,7 +24,7 @@
 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.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.Provider;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
@@ -95,7 +95,7 @@
 
   @Nullable
   @Override
-  public Info get(Provider.Key providerKey) {
+  public InfoInterface get(Provider.Key providerKey) {
     return actual.get(providerKey);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
index e18857f..016e05f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
@@ -40,7 +40,7 @@
 import com.google.devtools.build.lib.packages.AttributeMap;
 import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.BuiltinProvider;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.NativeProvider;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
@@ -101,7 +101,7 @@
     return builder.build();
   }
 
-  public static final <T extends Info> Iterable<T> getTransitivePrerequisites(
+  public static final <T extends InfoInterface> Iterable<T> getTransitivePrerequisites(
       RuleContext ruleContext, Mode mode, NativeProvider<T> key) {
     IterablesChain.Builder<T> builder = IterablesChain.builder();
     AttributeMap attributes = ruleContext.attributes();
@@ -113,7 +113,7 @@
     return builder.build();
   }
 
-  public static final <T extends Info> Iterable<T> getTransitivePrerequisites(
+  public static final <T extends InfoInterface> Iterable<T> getTransitivePrerequisites(
       RuleContext ruleContext, Mode mode, BuiltinProvider<T> key) {
     IterablesChain.Builder<T> builder = IterablesChain.builder();
     AttributeMap attributes = ruleContext.attributes();
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java b/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java
index 561843a..081a896 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/ApplePlatform.java
@@ -18,10 +18,10 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.events.Location;
-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.packages.SkylarkInfo;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.skylarkbuildapi.apple.ApplePlatformApi;
 import com.google.devtools.build.lib.skylarkbuildapi.apple.ApplePlatformTypeApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
@@ -175,8 +175,8 @@
   }
 
   /** Returns a Skylark struct that contains the instances of this enum. */
-  public static Info getSkylarkStruct() {
-    Provider constructor = new NativeProvider<Info>(Info.class, "platforms") {};
+  public static StructImpl getSkylarkStruct() {
+    Provider constructor = new NativeProvider<StructImpl>(StructImpl.class, "platforms") {};
     HashMap<String, Object> fields = new HashMap<>();
     for (ApplePlatform type : values()) {
       fields.put(type.skylarkKey, type);
@@ -231,8 +231,8 @@
     }
 
     /** Returns a Skylark struct that contains the instances of this enum. */
-    public static Info getSkylarkStruct() {
-      Provider constructor = new NativeProvider<Info>(Info.class, "platform_types") {};
+    public static StructImpl getSkylarkStruct() {
+      Provider constructor = new NativeProvider<StructImpl>(StructImpl.class, "platform_types") {};
       HashMap<String, Object> fields = new HashMap<>();
       for (PlatformType type : values()) {
         fields.put(type.skylarkKey, type);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagProvider.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagProvider.java
index b406183..cbeec71 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagProvider.java
@@ -95,7 +95,7 @@
   }
 
   // ConfigFeatureFlagProvider instances should all be unique, so we override the default
-  // equals and hashCode from Info to ensure that. SCO's toString is fine, however.
+  // equals and hashCode from InfoInterface to ensure that. SCO's toString is fine, however.
   @Override
   public boolean equals(Object other) {
     return other == this;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
index ab8fe68..ee615ba 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
@@ -27,12 +27,12 @@
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.Attribute.SplitTransitionProvider;
-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.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.packages.SkylarkAspect;
 import com.google.devtools.build.lib.packages.SkylarkInfo;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.rules.apple.AppleConfiguration;
 import com.google.devtools.build.lib.rules.apple.ApplePlatform;
 import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType;
@@ -83,8 +83,8 @@
   @VisibleForTesting
   public static final String MISSING_KEY_ERROR = "No value for required key %s was present.";
 
-  @Nullable private Info platformType;
-  @Nullable private Info platform;
+  @Nullable private StructImpl platformType;
+  @Nullable private StructImpl platform;
 
   private ObjcProtoAspect objcProtoAspect;
 
@@ -98,7 +98,7 @@
   }
 
   @Override
-  public Info getPlatformTypeStruct() {
+  public StructImpl getPlatformTypeStruct() {
     if (platformType == null) {
       platformType = PlatformType.getSkylarkStruct();
     }
@@ -106,7 +106,7 @@
   }
 
   @Override
-  public Info getPlatformStruct() {
+  public StructImpl getPlatformStruct() {
     if (platform == null) {
       platform = ApplePlatform.getSkylarkStruct();
     }
@@ -233,7 +233,7 @@
   }
 
   @Override
-  public Info linkMultiArchBinary(
+  public StructImpl linkMultiArchBinary(
       SkylarkRuleContextApi skylarkRuleContextApi, Environment environment)
       throws EvalException, InterruptedException {
     SkylarkRuleContext skylarkRuleContext = (SkylarkRuleContext) skylarkRuleContextApi;
@@ -260,9 +260,10 @@
    * Creates a Skylark struct that contains the results of the {@code link_multi_arch_binary}
    * function.
    */
-  private Info createAppleBinaryOutputSkylarkStruct(
+  private StructImpl createAppleBinaryOutputSkylarkStruct(
       AppleBinaryOutput output, Environment environment) {
-    Provider constructor = new NativeProvider<Info>(Info.class, "apple_binary_output") {};
+    Provider constructor =
+        new NativeProvider<StructImpl>(StructImpl.class, "apple_binary_output") {};
     // We have to transform the output group dictionary into one that contains SkylarkValues instead
     // of plain NestedSets because the Skylark caller may want to return this directly from their
     // implementation function.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
index b700620..d84fe5a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
@@ -30,8 +30,9 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 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.Info;
 import com.google.devtools.build.lib.packages.NativeProvider.WithLegacySkylarkName;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsStore;
 import com.google.devtools.build.lib.rules.cpp.CcLinkingInfo;
@@ -52,7 +53,7 @@
  * deps that are needed for building Objective-C rules.
  */
 @Immutable
-public final class ObjcProvider extends NativeInfo implements ObjcProviderApi<Artifact> {
+public final class ObjcProvider extends Info implements ObjcProviderApi<Artifact> {
 
   /** Skylark name for the ObjcProvider. */
   public static final String SKYLARK_NAME = "objc";
@@ -668,7 +669,7 @@
       ImmutableMap<Key<?>, NestedSet<?>> items,
       ImmutableMap<Key<?>, NestedSet<?>> nonPropagatedItems,
       ImmutableMap<Key<?>, NestedSet<?>> strictDependencyItems) {
-    super(SKYLARK_CONSTRUCTOR);
+    super(SKYLARK_CONSTRUCTOR, Location.BUILTIN);
     this.semantics = semantics;
     this.items = Preconditions.checkNotNull(items);
     this.nonPropagatedItems = Preconditions.checkNotNull(nonPropagatedItems);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java
index f7dc98a..7f49e5e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java
@@ -24,7 +24,7 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.packages.StructProvider;
 import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -168,7 +168,7 @@
     @SuppressWarnings("unchecked")
     @Override
     public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) {
-      NestedSetBuilder<Info> result = NestedSetBuilder.stableOrder();
+      NestedSetBuilder<StructImpl> result = NestedSetBuilder.stableOrder();
       for (BundleableFile bundleableFile : (Iterable<BundleableFile>) javaValue) {
         result.add(
             StructProvider.STRUCT.create(
@@ -177,15 +177,15 @@
                     BUNDLE_PATH_FIELD, bundleableFile.getBundlePath()),
                 "No such attribute '%s'"));
       }
-      return SkylarkNestedSet.of(Info.class, result.build());
+      return SkylarkNestedSet.of(StructImpl.class, result.build());
     }
 
     @SuppressWarnings("unchecked")
     @Override
     public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) {
-      validateTypes(skylarkValue, Info.class, javaKey.getSkylarkKeyName());
+      validateTypes(skylarkValue, StructImpl.class, javaKey.getSkylarkKeyName());
       NestedSetBuilder<BundleableFile> result = NestedSetBuilder.stableOrder();
-      for (Info struct : ((SkylarkNestedSet) skylarkValue).toCollection(Info.class)) {
+      for (StructImpl struct : ((SkylarkNestedSet) skylarkValue).toCollection(StructImpl.class)) {
         Artifact artifact;
         String path;
         try {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
index 158233a..4483d7e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
@@ -42,8 +42,8 @@
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.packages.BuildType;
-import com.google.devtools.build.lib.packages.Info;
 import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.packages.StructProvider;
 import com.google.devtools.build.lib.rules.cpp.CppFileTypes;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
@@ -179,7 +179,7 @@
    *
    * <p>addSkylarkTransitiveInfo(PYTHON_SKYLARK_PROVIDER_NAME, createSourceProvider(...))
    */
-  public static Info createSourceProvider(
+  public static StructImpl createSourceProvider(
       NestedSet<Artifact> transitivePythonSources, boolean isUsingSharedLibrary) {
     return StructProvider.STRUCT.create(
         ImmutableMap.<String, Object>of(
@@ -320,12 +320,12 @@
 
   private NestedSet<Artifact> getTransitivePythonSourcesFromSkylarkProvider(
       TransitiveInfoCollection dep) {
-    Info pythonSkylarkProvider = null;
+    StructImpl pythonSkylarkProvider = null;
     try {
       pythonSkylarkProvider =
           SkylarkType.cast(
               dep.get(PYTHON_SKYLARK_PROVIDER_NAME),
-              Info.class,
+              StructImpl.class,
               null,
               "%s should be a struct",
               PYTHON_SKYLARK_PROVIDER_NAME);
@@ -500,8 +500,8 @@
     for (TransitiveInfoCollection dep : deps) {
       Object providerObject = dep.get(PYTHON_SKYLARK_PROVIDER_NAME);
       if (providerObject != null) {
-        SkylarkType.checkType(providerObject, Info.class, null);
-        Info provider = (Info) providerObject;
+        SkylarkType.checkType(providerObject, StructImpl.class, null);
+        StructImpl provider = (StructImpl) providerObject;
         Boolean isUsingSharedLibrary = provider.getValue(IS_USING_SHARED_LIBRARY, Boolean.class);
         if (Boolean.TRUE.equals(isUsingSharedLibrary)) {
           return true;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java
index 2c95721..12a3e98 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java
@@ -26,8 +26,9 @@
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.AspectDescriptor;
 import com.google.devtools.build.lib.packages.AspectParameters;
-import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.InfoInterface;
 import com.google.devtools.build.lib.packages.SkylarkDefinedAspect;
+import com.google.devtools.build.lib.packages.StructImpl;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
 import com.google.devtools.build.lib.syntax.Environment;
@@ -84,7 +85,7 @@
 
         if (ruleContext.hasErrors()) {
           return null;
-        } else if (!(aspectSkylarkObject instanceof Info)
+        } else if (!(aspectSkylarkObject instanceof StructImpl)
             && !(aspectSkylarkObject instanceof Iterable)) {
           ruleContext.ruleError(
               String.format(
@@ -114,7 +115,7 @@
     if (aspectSkylarkObject instanceof Iterable) {
       addDeclaredProviders(builder, (Iterable) aspectSkylarkObject);
     } else {
-      Info struct = (Info) aspectSkylarkObject;
+      StructImpl struct = (StructImpl) aspectSkylarkObject;
       Location loc = struct.getCreationLoc();
       for (String field : struct.getFieldNames()) {
         if (field.equals("output_groups")) {
@@ -146,10 +147,10 @@
     int i = 0;
     for (Object o : aspectSkylarkObject) {
       Location loc = skylarkAspect.getImplementation().getLocation();
-      Info declaredProvider =
+      InfoInterface declaredProvider =
           SkylarkType.cast(
               o,
-              Info.class,
+              InfoInterface.class,
               loc,
               "A return value of an aspect implementation function should be "
                   + "a sequence of declared providers, instead got a %s at index %d",
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java
index 1798ec6..e4eea4c 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple/ObjcProviderApi.java
@@ -16,10 +16,10 @@
 
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
-import com.google.devtools.build.lib.skylarkbuildapi.StructApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
 import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
 
 /**
@@ -31,7 +31,7 @@
     category = SkylarkModuleCategory.PROVIDER,
     doc = "A provider for compilation and linking of objc."
 )
-public interface ObjcProviderApi<FileApiT extends FileApi> extends StructApi {
+public interface ObjcProviderApi<FileApiT extends FileApi> extends SkylarkValue {
 
   @SkylarkCallable(name = "asset_catalog",
       structField = true,
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java
index 5ade8bf..475de33 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/apple/FakeObjcProvider.java
@@ -15,11 +15,9 @@
 package com.google.devtools.build.skydoc.fakebuildapi.apple;
 
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
 import com.google.devtools.build.lib.skylarkbuildapi.apple.ObjcProviderApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
-import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
 
 /**
@@ -218,15 +216,5 @@
   }
 
   @Override
-  public String toProto(Location loc) throws EvalException {
-    return "";
-  }
-
-  @Override
-  public String toJson(Location loc) throws EvalException {
-    return "";
-  }
-
-  @Override
   public void repr(SkylarkPrinter printer) {}
 }