[go: nahoru, domu]

Improve stable/static analysis

Change-Id: Id201bd133d3fc0628b827f1118f9bf29e0ed7505
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt
index ad83f29..9c2778c 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt
@@ -93,20 +93,17 @@
             fun Example(foo: Foo, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) {
               %composer.startRestartGroup(%key)
               val %dirty = %changed
-              val foo = foo
-              if (%changed and 0b0110 === 0) {
-                %dirty = %dirty or if (%default and 0b0001 === 0 && %composer.changed(foo)) 0b0100 else 0b0010
+              val foo = if (%default and 0b0001 !== 0) {
+                Foo(0)
+              } else {
+                foo
+              }
+              if (%default and 0b0001 !== 0) {
+                %dirty = %dirty or 0b0110
+              } else if (%changed and 0b0110 === 0) {
+                %dirty = %dirty or if (%composer.changed(foo)) 0b0100 else 0b0010
               }
               if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
-                if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) {
-                  %composer.startDefaults()
-                  if (%default and 0b0001 !== 0) {
-                    foo = Foo(0)
-                  }
-                  %composer.endDefaults()
-                } else {
-                  %composer.skipCurrentGroup()
-                }
                 print(foo)
               } else {
                 %composer.skipToGroupEnd()
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index acc6bdb..1003027 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -1260,12 +1260,24 @@
             interface Modifier {
               companion object : Modifier { }
             }
+            inline class Dp(val value: Int)
+            @Stable
+            fun stableFun(x: Int): Int = x * x
+            @Stable
+            operator fun Dp.plus(other: Dp): Dp = Dp(this.value + other.value)
+            @Stable
+            val Int.dp: Dp get() = Dp(this)
         """,
         """
             // all of these should result in 0b0110
             @Composable fun A() {
                 val x = 123
+                C(stableFun(123))
+                C(16.dp + 10.dp)
+                C(Dp(16))
+                C(16.dp)
                 C(normInt)
+                C(Int.MAX_VALUE)
                 C(stableTopLevelProp)
                 C(Modifier)
                 C(Foo.Bar)
@@ -1287,7 +1299,12 @@
               %composer.startRestartGroup(%key)
               if (%changed !== 0 || !%composer.skipping) {
                 val x = 123
+                C(stableFun(123), %composer, <>, 0b0110)
+                C(16.dp + 10.dp, %composer, <>, 0b0110)
+                C(Dp(16), %composer, <>, 0b0110)
+                C(16.dp, %composer, <>, 0b0110)
                 C(normInt, %composer, <>, 0b0110)
+                C(Companion.MAX_VALUE, %composer, <>, 0b0110)
                 C(stableTopLevelProp, %composer, <>, 0b0110)
                 C(Companion, %composer, <>, 0b0110)
                 C(Foo.Bar, %composer, <>, 0b0110)
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
index 38ba7a6..ab94fe0 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
@@ -38,6 +38,7 @@
     val Direct = ComposeUtils.composeFqName("Direct")
     val key = ComposeUtils.composeFqName("key")
     val StableMarker = ComposeUtils.composeFqName("StableMarker")
+    val Stable = ComposeUtils.composeFqName("Stable")
     val Composer = ComposeUtils.composeFqName("Composer")
     val Untracked = ComposeUtils.composeFqName("Untracked")
     val UiComposer = FqName.fromSegments(listOf("androidx", "ui", "node", "UiComposer"))
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt
index 5529993..36c352b 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt
@@ -26,6 +26,7 @@
 import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType
 import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
 import org.jetbrains.kotlin.builtins.KotlinBuiltIns
 import org.jetbrains.kotlin.builtins.extractParameterNameFromFunctionTypeArgument
 import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
@@ -96,12 +97,16 @@
 import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
 import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
 import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
+import org.jetbrains.kotlin.ir.symbols.IrSymbol
 import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
 import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
 import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
 import org.jetbrains.kotlin.ir.types.IrType
 import org.jetbrains.kotlin.ir.types.classOrNull
 import org.jetbrains.kotlin.ir.types.defaultType
+import org.jetbrains.kotlin.ir.types.isNullable
+import org.jetbrains.kotlin.ir.types.isPrimitiveType
+import org.jetbrains.kotlin.ir.types.makeNullable
 import org.jetbrains.kotlin.ir.types.toKotlinType
 import org.jetbrains.kotlin.ir.util.ConstantValueGenerator
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
@@ -125,6 +130,7 @@
 import org.jetbrains.kotlin.types.isNullable
 import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
 import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
+import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
 
 abstract class AbstractComposeLowering(
     val context: IrPluginContext,
@@ -197,10 +203,38 @@
 
     fun KotlinType.toIrType(): IrType = typeTranslator.translateType(this)
 
+    fun IrType.unboxInlineClass() = unboxType() ?: this
+
+    fun <T : IrSymbol> T.bindIfNecessary(): T {
+        if (!isBound) {
+            context.irProviders.firstNotNullResult { it.getDeclaration(this) }
+        }
+        return this
+    }
+
+    // NOTE(lmr): This implementation mimics the kotlin-provided unboxInlineClass method, except
+    // this one makes sure to bind the symbol if it is unbound, so is a bit safer to use.
+    fun IrType.unboxType(): IrType? {
+        val classSymbol = classOrNull ?: return null
+        val klass = classSymbol.bindIfNecessary().owner
+        if (!klass.isInline) return null
+
+        // TODO: Apply type substitutions
+        val underlyingType = InlineClassAbi.getUnderlyingType(klass).unboxInlineClass()
+        if (!isNullable()) return underlyingType
+        if (underlyingType.isNullable() || underlyingType.isPrimitiveType())
+            return null
+        return underlyingType.makeNullable()
+    }
+
     fun IrAnnotationContainer.hasComposableAnnotation(): Boolean {
         return annotations.hasAnnotation(ComposeFqNames.Composable)
     }
 
+    fun IrAnnotationContainer.hasStableAnnotation(): Boolean {
+        return annotations.hasAnnotation(ComposeFqNames.Stable)
+    }
+
     fun List<IrConstructorCall>.hasAnnotation(fqName: FqName): Boolean =
         any { it.symbol.descriptor.constructedClass.fqNameSafe == fqName }
 
@@ -434,7 +468,7 @@
         extensionReceiver: IrExpression? = null,
         vararg args: IrExpression
     ): IrCallImpl {
-        context.irProviders.getDeclaration(symbol)
+        symbol.bindIfNecessary()
         return IrCallImpl(
             UNDEFINED_OFFSET,
             UNDEFINED_OFFSET,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt
index 343aa5d..0573b49 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt
@@ -74,6 +74,7 @@
 import org.jetbrains.kotlin.ir.expressions.IrCall
 import org.jetbrains.kotlin.ir.expressions.IrConst
 import org.jetbrains.kotlin.ir.expressions.IrConstKind
+import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
 import org.jetbrains.kotlin.ir.expressions.IrDoWhileLoop
 import org.jetbrains.kotlin.ir.expressions.IrElseBranch
 import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -114,8 +115,10 @@
 import org.jetbrains.kotlin.ir.types.makeNullable
 import org.jetbrains.kotlin.ir.types.toKotlinType
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
+import org.jetbrains.kotlin.ir.util.getArguments
 import org.jetbrains.kotlin.ir.util.getPropertyGetter
 import org.jetbrains.kotlin.ir.util.hasDefaultValue
+import org.jetbrains.kotlin.ir.util.isInlined
 import org.jetbrains.kotlin.ir.util.isVararg
 import org.jetbrains.kotlin.ir.util.patchDeclarationParents
 import org.jetbrains.kotlin.ir.util.statements
@@ -2312,6 +2315,18 @@
             // Getting a companion object or top level object can be considered static if the
             // type of that object is Stable. (`Modifier` for instance is a common example)
             is IrGetObjectValue -> symbol.owner.superTypes.any { it.toKotlinType().isStable() }
+            is IrConstructorCall -> {
+                // special case constructors of inline classes as static if their underlying
+                // value is static.
+                if (
+                    type.isInlined() &&
+                    type.unboxInlineClass().toKotlinType().isStable() &&
+                    getValueArgument(0)?.isStatic() == true
+                ) {
+                    return true
+                }
+                false
+            }
             is IrCall -> when (origin) {
                 is IrStatementOrigin.GET_PROPERTY -> {
                     // If we are in a GET_PROPERTY call, then this should usually resolve to
@@ -2322,13 +2337,34 @@
                     // if the property is a top level constant, then it is static.
                     if (prop.isConst) return true
 
+                    val typeIsStable = type.toKotlinType().isStable()
+                    val dispatchReceiverIsStatic = dispatchReceiver?.isStatic() != false
+                    val extensionReceiverIsStatic = extensionReceiver?.isStatic() != false
+
                     // if we see that the property is read-only with a default getter and a
                     // stable return type , then reading the property can also be considered
                     // static if this is a top level property or the subject is also static.
-                    !prop.isVar &&
+                    if (!prop.isVar &&
                             prop.getter?.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR &&
-                            type.toKotlinType().isStable() &&
-                            (dispatchReceiver == null || dispatchReceiver?.isStatic() == true)
+                        typeIsStable &&
+                        dispatchReceiverIsStatic && extensionReceiverIsStatic
+                    ) {
+                        return true
+                    }
+
+                    val getterIsStable = prop.hasStableAnnotation() ||
+                            symbol.owner.hasStableAnnotation()
+
+                    if (
+                        getterIsStable &&
+                        typeIsStable &&
+                        dispatchReceiverIsStatic &&
+                        extensionReceiverIsStatic
+                    ) {
+                        return true
+                    }
+
+                    false
                 }
                 is IrStatementOrigin.PLUS,
                 is IrStatementOrigin.MUL,
@@ -2346,9 +2382,32 @@
                     // special case mathematical operators that are in the stdlib. These are
                     // immutable operations so the overall result is static if the operands are
                     // also static
-                    symbol.descriptor.fqNameSafe.topLevelName() == "kotlin" &&
-                    dispatchReceiver?.isStatic() == true &&
-                    getValueArgument(0)?.isStatic() == true
+                    val isStableOperator = symbol
+                        .descriptor
+                        .fqNameSafe
+                        .topLevelName() == "kotlin" ||
+                            symbol.owner.hasStableAnnotation()
+
+                    val typeIsStable = type.toKotlinType().isStable()
+                    if (!typeIsStable) return false
+
+                    if (!isStableOperator) {
+                        return false
+                    }
+
+                    getArguments().all { it.second.isStatic() }
+                }
+                null -> {
+                    // normal function call. If the function is marked as Stable and the result
+                    // is Stable, then the static-ness of it is the static-ness of its arguments
+                    val isStable = symbol.owner.hasStableAnnotation()
+                    if (!isStable) return false
+
+                    val typeIsStable = type.toKotlinType().isStable()
+                    if (!typeIsStable) return false
+
+                    // getArguments includes the receivers!
+                    getArguments().all { it.second.isStatic() }
                 }
                 else -> false
             }
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
index 6d9778da..778f493 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
@@ -247,10 +247,7 @@
         startOffset: Int = UNDEFINED_OFFSET,
         endOffset: Int = UNDEFINED_OFFSET
     ): IrExpression {
-        val classSymbol = classOrNull
-        if (classSymbol?.isBound == false) {
-            context.irProviders.getDeclaration(classSymbol)
-        }
+        val classSymbol = classOrNull?.bindIfNecessary()
         if (this !is IrSimpleType || hasQuestionMark || classSymbol?.owner?.isInline != true)
             return IrConstImpl.defaultValueForType(startOffset, endOffset, this)
 
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt
index eab1be1..788188f 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt
@@ -331,16 +331,16 @@
                 // unary prefx
                 "unaryPlus", "unaryMinus", "not" -> {
                     print(opSymbol)
-                    expression.dispatchReceiver?.print()
+                    (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                 }
                 // unary postfix
                 "inc", "dec" -> {
-                    expression.dispatchReceiver?.print()
+                    (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                     print(opSymbol)
                 }
                 // invoke
                 "invoke" -> {
-                    expression.dispatchReceiver?.print()
+                    (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                     expression.printArgumentList()
                 }
                 // get indexer
@@ -355,7 +355,7 @@
                 }
                 // set indexer
                 "set" -> {
-                    expression.dispatchReceiver?.print()
+                    (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                     print("[")
                     expression.getValueArgument(0)?.print()
                     print("] = ")
@@ -368,14 +368,14 @@
                     expression.getValueArgument(1)?.print()
                 }
                 "iterator", "hasNext", "next" -> {
-                    expression.dispatchReceiver?.print()
+                    (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                     print(".")
                     print(opSymbol)
                     print("()")
                 }
                 // else binary
                 else -> {
-                    expression.dispatchReceiver?.print()
+                    (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                     print(" $opSymbol ")
                     expression.getValueArgument(0)?.print()
                 }
diff --git a/compose/compose-runtime/api/0.1.0-dev14.txt b/compose/compose-runtime/api/0.1.0-dev14.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/0.1.0-dev14.txt
+++ b/compose/compose-runtime/api/0.1.0-dev14.txt
@@ -80,7 +80,7 @@
   }
 
   public final class ComposeKt {
-    method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
     method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
@@ -482,7 +482,7 @@
     property public final int parentNodes;
   }
 
-  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/current.txt b/compose/compose-runtime/api/current.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/current.txt
+++ b/compose/compose-runtime/api/current.txt
@@ -80,7 +80,7 @@
   }
 
   public final class ComposeKt {
-    method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
     method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
@@ -482,7 +482,7 @@
     property public final int parentNodes;
   }
 
-  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt
@@ -80,7 +80,7 @@
   }
 
   public final class ComposeKt {
-    method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
     method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
@@ -482,7 +482,7 @@
     property public final int parentNodes;
   }
 
-  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/public_plus_experimental_current.txt b/compose/compose-runtime/api/public_plus_experimental_current.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/public_plus_experimental_current.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_current.txt
@@ -80,7 +80,7 @@
   }
 
   public final class ComposeKt {
-    method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
     method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
@@ -482,7 +482,7 @@
     property public final int parentNodes;
   }
 
-  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev14.txt b/compose/compose-runtime/api/restricted_0.1.0-dev14.txt
index fd7a436..9ccc3fe 100644
--- a/compose/compose-runtime/api/restricted_0.1.0-dev14.txt
+++ b/compose/compose-runtime/api/restricted_0.1.0-dev14.txt
@@ -80,7 +80,7 @@
   }
 
   public final class ComposeKt {
-    method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
     method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
@@ -511,7 +511,7 @@
     property public final int parentNodes;
   }
 
-  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/restricted_current.txt b/compose/compose-runtime/api/restricted_current.txt
index fd7a436..9ccc3fe 100644
--- a/compose/compose-runtime/api/restricted_current.txt
+++ b/compose/compose-runtime/api/restricted_current.txt
@@ -80,7 +80,7 @@
   }
 
   public final class ComposeKt {
-    method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
     method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
@@ -511,7 +511,7 @@
     property public final int parentNodes;
   }
 
-  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+  @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
index a1a6942..cf01af8 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
@@ -23,6 +23,7 @@
  *
  * See [orEmpty] for handling nullable Composable lambdas using empty content.
  */
+@Stable
 fun emptyContent() = EmptyComposable
 
 /**
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt
index 1ea88be..3247a11 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt
@@ -16,8 +16,34 @@
 
 package androidx.compose
 
+/**
+ * Stable is used to communicate some guarantees to the compose compiler about how a certain type
+ * or function will behave.
+ *
+ * When applied to a class or an interface, [Stable] indicates that the following must be true:
+ *
+ *   1) The result of [equals] will always return the same result for the same two instances.
+ *   2) When a public property of the type changes, composition will be notified.
+ *   3) All public property types are stable.
+ *
+ * When applied to a function or a property, the [Stable] annotation indicates that the function
+ * will return the same result if the same parameters are passed in. This is only meaningful if
+ * the parameters and results are themselves [Stable], [Immutable], or primitive.
+ *
+ * The invariants that this annotation implies are used for optimizations by the compose compiler,
+ * and have undefined behavior if the above assumptions are not met. As a result, one should not
+ * use this annotation unless they are certain that these conditions are satisfied.
+ *
+ * @see Immutable
+ * @see StableMarker
+ */
 @MustBeDocumented
-@Target(AnnotationTarget.CLASS)
+@Target(
+    AnnotationTarget.CLASS,
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY_GETTER,
+    AnnotationTarget.PROPERTY
+)
 @Retention(AnnotationRetention.BINARY)
 @StableMarker
 annotation class Stable
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt b/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt
index d455b19..ecb3d4f 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt
@@ -17,6 +17,7 @@
 package androidx.ui.core
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.IntPx
 import androidx.ui.unit.IntPxPosition
 import androidx.ui.unit.IntPxSize
@@ -68,24 +69,39 @@
 
     companion object {
         // 2D Alignments.
+        @Stable
         val TopStart: Alignment = DirectionalAlignment(-1f, -1f)
+        @Stable
         val TopCenter: Alignment = DirectionalAlignment(-1f, 0f)
+        @Stable
         val TopEnd: Alignment = DirectionalAlignment(-1f, 1f)
+        @Stable
         val CenterStart: Alignment = DirectionalAlignment(0f, -1f)
+        @Stable
         val Center: Alignment = DirectionalAlignment(0f, 0f)
+        @Stable
         val CenterEnd: Alignment = DirectionalAlignment(0f, 1f)
+        @Stable
         val BottomStart: Alignment = DirectionalAlignment(1f, -1f)
+        @Stable
         val BottomCenter: Alignment = DirectionalAlignment(1f, 0f)
+        @Stable
         val BottomEnd: Alignment = DirectionalAlignment(1f, 1f)
 
         // 1D Alignment.Verticals.
+        @Stable
         val Top: Vertical = DirectionalAlignment.Vertical(-1f)
+        @Stable
         val CenterVertically: Vertical = DirectionalAlignment.Vertical(0f)
+        @Stable
         val Bottom: Vertical = DirectionalAlignment.Vertical(1f)
 
         // 1D Alignment.Horizontals.
+        @Stable
         val Start: Horizontal = DirectionalAlignment.Horizontal(-1f)
+        @Stable
         val CenterHorizontally: Horizontal = DirectionalAlignment.Horizontal(0f)
+        @Stable
         val End: Horizontal = DirectionalAlignment.Horizontal(1f)
     }
 }
@@ -188,15 +204,23 @@
 
     companion object {
         // 2D AbsoluteAlignments.
+        @Stable
         val TopLeft = AbsoluteAlignment(-1f, -1f)
+        @Stable
         val TopRight = AbsoluteAlignment(-1f, 1f)
+        @Stable
         val CenterLeft = AbsoluteAlignment(0f, -1f)
+        @Stable
         val CenterRight = AbsoluteAlignment(0f, 1f)
+        @Stable
         val BottomLeft = AbsoluteAlignment(1f, -1f)
+        @Stable
         val BottomRight = AbsoluteAlignment(1f, 1f)
 
         // 1D AbsoluteAlignment.Horizontals.
+        @Stable
         val Left: Horizontal = Horizontal(-1f)
+        @Stable
         val Right: Horizontal = Horizontal(1f)
     }
 }
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt b/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt
index a4f038a..b1005b5 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt
@@ -16,6 +16,7 @@
 
 package androidx.ui.core
 
+import androidx.compose.Immutable
 import androidx.ui.unit.IntPx
 
 /**
@@ -46,6 +47,7 @@
  * @param merger Defines the position of an alignment line inherited from more than one child.
  * @param horizontal Whether the alignment line is horizontal or vertical.
  */
+@Immutable
 sealed class AlignmentLine(
     internal val merger: (IntPx, IntPx) -> IntPx
 )
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt b/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt
index ce25f98..5d032b7 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt
@@ -16,11 +16,13 @@
 
 package androidx.ui.core
 
+import androidx.compose.Stable
 import androidx.ui.graphics.Shape
 
 /**
  * Clip the content to the bounds of a layer defined at this modifier.
  */
+@Stable
 fun Modifier.clipToBounds() = drawLayer(clip = true)
 
 /**
@@ -28,4 +30,5 @@
  *
  * @param shape the content will be clipped to this [Shape].
  */
+@Stable
 fun Modifier.clip(shape: Shape) = drawLayer(clip = true, shape = shape)
\ No newline at end of file
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt b/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt
index f9163dd..4e6b22a 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.core
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.geometry.Size
 import kotlin.math.max
 import kotlin.math.min
@@ -25,6 +27,7 @@
 /**
  * Represents a rule to apply to scale a source rectangle to be inscribed into a destination
  */
+@Stable
 interface ContentScale {
 
     /**
@@ -46,6 +49,7 @@
          * This [ContentScale] implementation in combination with usage of [Alignment.Center]
          * provides similar behavior to [android.widget.ImageView.ScaleType.CENTER_CROP]
          */
+        @Stable
         val Crop = object : ContentScale {
             override fun scale(srcSize: Size, dstSize: Size): Float =
                 computeFillMaxDimension(srcSize, dstSize)
@@ -59,6 +63,7 @@
          * This [ContentScale] implementation in combination with usage of [Alignment.Center]
          * provides similar behavior to [android.widget.ImageView.ScaleType.FIT_CENTER]
          */
+        @Stable
         val Fit = object : ContentScale {
             override fun scale(srcSize: Size, dstSize: Size): Float =
                 computeFillMinDimension(srcSize, dstSize)
@@ -69,6 +74,7 @@
          * height. This can cover a larger area than the destination if the height is larger than
          * the width.
          */
+        @Stable
         val FillHeight = object : ContentScale {
             override fun scale(srcSize: Size, dstSize: Size): Float =
                 computeFillHeight(srcSize, dstSize)
@@ -79,6 +85,7 @@
          * destination width. This can cover a larger area than the destination if the width is
          * larger than the height.
          */
+        @Stable
         val FillWidth = object : ContentScale {
             override fun scale(srcSize: Size, dstSize: Size): Float =
                 computeFillWidth(srcSize, dstSize)
@@ -93,6 +100,7 @@
          * This [ContentScale] implementation in combination with usage of [Alignment.Center]
          * provides similar behavior to [android.widget.ImageView.ScaleType.CENTER_INSIDE]
          */
+        @Stable
         val Inside = object : ContentScale {
             override fun scale(srcSize: Size, dstSize: Size): Float =
                 if (srcSize.width <= dstSize.width && srcSize.height <= dstSize.height) {
@@ -105,6 +113,7 @@
         /**
          * Do not apply any scaling to the source
          */
+        @Stable
         val None = FixedScale(OriginalScale)
     }
 }
@@ -113,6 +122,7 @@
  * [ContentScale] implementation that always scales the dimension by the provided
  * fixed floating point value
  */
+@Immutable
 data class FixedScale(val value: Float) : ContentScale {
     override fun scale(srcSize: Size, dstSize: Size): Float = value
 }
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt b/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt
index 38f42dc..40a349e 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt
@@ -18,6 +18,7 @@
 
 import androidx.annotation.FloatRange
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.graphics.RectangleShape
 import androidx.ui.graphics.Shape
 import androidx.ui.util.packFloats
@@ -176,6 +177,7 @@
  * @param shape [DrawLayerModifier.shape]
  * @param clip [DrawLayerModifier.clip]
  */
+@Stable
 fun Modifier.drawLayer(
     scaleX: Float = 1f,
     scaleY: Float = 1f,
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt b/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt
index 0dfb726..6c29b8a 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt
@@ -17,6 +17,7 @@
 package androidx.ui.core
 
 import androidx.annotation.FloatRange
+import androidx.compose.Stable
 import androidx.ui.graphics.RectangleShape
 import androidx.ui.graphics.Shape
 import androidx.ui.unit.Dp
@@ -39,6 +40,7 @@
  * @param clip When active, the content drawing clips to the shape.
  * @param opacity The opacity of the layer, including the shadow.
  */
+@Stable
 fun Modifier.drawShadow(
     elevation: Dp,
     shape: Shape = RectangleShape,
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt b/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt
index 5b01bc4..f3eafe6 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt
@@ -17,11 +17,13 @@
 package androidx.ui.core
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.Density
 
 /**
  * Tag the element with [tag] to identify the element within its parent.
  */
+@Stable
 fun Modifier.tag(tag: Any) = this + LayoutTag(tag)
 
 /**
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt b/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt
index 4e4d5a5..e5db024 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt
@@ -17,6 +17,7 @@
 package androidx.ui.core
 
 import androidx.annotation.FloatRange
+import androidx.compose.Stable
 
 /**
  * Draw content with modified opacity (alpha) that may be less than 1.
@@ -26,6 +27,7 @@
  *
  * @param opacity the fraction of children's alpha value.
  */
+@Stable
 fun Modifier.drawOpacity(
     @FloatRange(from = 0.0, to = 1.0) opacity: Float
 ) = drawLayer(alpha = opacity, clip = true)
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt b/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt
index d324768..deec36c 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.core
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.IntPxSize
 import androidx.ui.unit.Px
 import androidx.ui.unit.PxPosition
@@ -45,6 +47,7 @@
  * @param previous The [PointerInputData] that represents the previous state of this pointer.
  * @param consumed Which aspects of this change have been consumed.
  */
+@Immutable
 data class PointerInputChange(
     val id: PointerId,
     val current: PointerInputData,
@@ -68,9 +71,13 @@
  * the owning [PointerInputChange] is being dispatched to.
  * @param down True if the at [uptime] the pointer was contacting the screen.
  */
+@Immutable
 data class PointerInputData(
+    @Stable
     val uptime: Uptime? = null,
+    @Stable
     val position: PxPosition? = null,
+    @Stable
     val down: Boolean = false
 )
 
@@ -80,6 +87,7 @@
  * @param positionChange The amount of change to the position that has been consumed.
  * @param downChange True if a change to down or up has been consumed.
  */
+@Immutable
 data class ConsumedData(
     val positionChange: PxPosition = PxPosition.Origin,
     val downChange: Boolean = false
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt b/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt
index 1abf7b6..f48f467 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt
@@ -17,6 +17,7 @@
 package androidx.ui.core
 
 import androidx.compose.Composable
+import androidx.compose.Stable
 import androidx.ui.core.semantics.semantics
 import androidx.ui.semantics.Semantics
 import androidx.ui.semantics.SemanticsPropertyReceiver
@@ -43,6 +44,7 @@
  * Note that this does not, by itself, create a semantics boundary. If the element you are
  * setting this on does not already create a boundary, you will need to create one.
  */
+@Stable
 fun Modifier.testTag(tag: String) = semantics(
     properties = {
         testTag = tag
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt b/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt
index ff88370..91658d0 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.core
 
+import androidx.compose.Stable
+
 /**
  * A [Modifier.Element] that controls the drawing order for the children of the same layout
  * parent. A child with larger [zIndex] will be drawn after all the children with smaller [zIndex].
@@ -42,6 +44,7 @@
  *
  * @sample androidx.ui.core.samples.ZIndexModifierSample
  */
+@Stable
 fun Modifier.zIndex(zIndex: Float): Modifier = this + SimpleZIndexModifier(zIndex)
 
 private data class SimpleZIndexModifier(override val zIndex: Float) : ZIndexModifier
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt
index 010c508..abfaa25 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt
@@ -17,6 +17,7 @@
 package androidx.ui.foundation
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.graphics.Brush
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.SolidColor
@@ -37,4 +38,5 @@
  * @param size size of the border in [Dp]. Use [Dp.Hairline] for one-pixel border.
  * @param color color to paint the border with
  */
+@Stable
 fun Border(size: Dp, color: Color) = Border(size, SolidColor(color))
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt
index 45e1c65..6526b56 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt
@@ -17,6 +17,8 @@
 package androidx.ui.foundation
 
 import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.compose.getValue
 import androidx.compose.mutableStateOf
 import androidx.compose.remember
@@ -57,8 +59,11 @@
  * @param selection the selection range. If the selection is collapsed, it represents cursor
  * location. Do not specify outside of the text buffer.
  */
+@Immutable
 data class TextFieldValue(
+    @Stable
     val text: String = "",
+    @Stable
     val selection: TextRange = TextRange(0, 0)
 ) {
     companion object {
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt
index c04b17a..b770082 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt
@@ -19,12 +19,15 @@
 import androidx.annotation.FloatRange
 import androidx.annotation.IntRange
 import androidx.ui.geometry.Size
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.Density
 import androidx.ui.unit.Dp
 
 /**
  * Defines size of a corner in pixels. For example for rounded shape it can be a corner radius.
  */
+@Immutable
 interface CornerSize {
     /**
      * Converts the [CornerSize] to pixels.
@@ -41,6 +44,7 @@
  * Creates [CornerSize] with provided size.
  * @param size the corner size defined in [Dp].
  */
+@Stable
 fun CornerSize(size: Dp): CornerSize = DpCornerSize(size)
 
 private data class DpCornerSize(private val size: Dp) : CornerSize {
@@ -52,6 +56,7 @@
  * Creates [CornerSize] with provided size.
  * @param size the corner size defined in pixels.
  */
+@Stable
 fun CornerSize(size: Float): CornerSize = PxCornerSize(size)
 
 private data class PxCornerSize(private val size: Float) : CornerSize {
@@ -63,6 +68,7 @@
  * @param percent the corner size defined in percents of the shape's smaller side.
  * Can't be negative or larger then 50 percents.
  */
+@Stable
 fun CornerSize(@IntRange(from = 0, to = 50) percent: Int): CornerSize =
     PercentCornerSize(percent.toFloat())
 
@@ -87,6 +93,7 @@
 /**
  * [CornerSize] always equals to zero.
  */
+@Stable
 val ZeroCornerSize: CornerSize = object : CornerSize {
     override fun toPx(shapeSize: Size, density: Density) = 0.0f
 }
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt
index 2780b7e..af6c248 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt
@@ -17,6 +17,7 @@
 package androidx.ui.geometry
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 import androidx.ui.util.packFloats
 import androidx.ui.util.toStringAsFixed
@@ -60,9 +61,11 @@
 @Immutable
 inline class Offset(@PublishedApi internal val packedValue: Long) {
 
+    @Stable
     val dx: Float
         get() = unpackFloat1(packedValue)
 
+    @Stable
     val dy: Float
         get() = unpackFloat2(packedValue)
 
@@ -72,6 +75,7 @@
          *
          * This can be used to represent the origin of a coordinate space.
          */
+        @Stable
         val zero = Offset(0.0f, 0.0f)
 
         /**
@@ -83,6 +87,7 @@
          *  * [isFinite], which checks whether both components are finite.
          */
         // This is included for completeness, because [Size.infinite] exists.
+        @Stable
         val infinite = Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
 
         /**
@@ -100,6 +105,7 @@
          * Values for [fraction] are usually obtained from an [Animation<Float>], such as
          * an `AnimationController`.
          */
+        @Stable
         fun lerp(start: Offset, stop: Offset, fraction: Float): Offset {
             return Offset(
                 lerp(start.dx, stop.dx, fraction),
@@ -107,6 +113,7 @@
             )
         }
 
+        @Stable
         fun isValid(offset: Offset): Boolean {
             check(!offset.dx.isNaN() && !offset.dy.isNaN()) {
                 "Offset argument contained a NaN value."
@@ -121,6 +128,7 @@
      * If you need this value to compare it to another [Offset]'s distance,
      * consider using [getDistanceSquared] instead, since it is cheaper to compute.
      */
+    @Stable
     fun getDistance() = sqrt(dx * dx + dy * dy)
 
     /**
@@ -128,6 +136,7 @@
      *
      * This is cheaper than computing the [getDistance] itself.
      */
+    @Stable
     fun getDistanceSquared() = dx * dx + dy * dy
 
     /**
@@ -181,6 +190,7 @@
      * Offset b = -a; // same as: a.scale(-1.0, -1.0)
      * ```
      */
+    @Stable
     fun scale(scaleX: Float, scaleY: Float): Offset = Offset(dx * scaleX, dy * scaleY)
 
     /**
@@ -197,6 +207,7 @@
      * Offset d = a - b; // same as: a.translate(-b.dx, -b.dy)
      * ```
      */
+    @Stable
     fun translate(translateX: Float, translateY: Float): Offset =
         Offset(dx + translateX, dy + translateY)
 
@@ -208,6 +219,7 @@
      * If the [Offset] represents an arrow on a plane, this operator returns the
      * same arrow but pointing in the reverse direction.
      */
+    @Stable
     operator fun unaryMinus(): Offset = Offset(-dx, -dy)
 
     /**
@@ -219,6 +231,7 @@
      *
      * See also [translate].
      */
+    @Stable
     operator fun minus(other: Offset): Offset = Offset(dx - other.dx, dy - other.dy)
 
     /**
@@ -230,6 +243,7 @@
      *
      * See also [translate].
      */
+    @Stable
     operator fun plus(other: Offset): Offset = Offset(dx + other.dx, dy + other.dy)
 
     /**
@@ -241,6 +255,7 @@
      *
      * See also [scale].
      */
+    @Stable
     operator fun times(operand: Float): Offset = Offset(dx * operand, dy * operand)
 
     /**
@@ -252,6 +267,7 @@
      *
      * See also [scale].
      */
+    @Stable
     operator fun div(operand: Float): Offset = Offset(dx / operand, dy / operand)
 
     /**
@@ -272,6 +288,7 @@
      * coordinates of the left-hand-side operand (an Offset) by the scalar
      * right-hand-side operand (a Float).
      */
+    @Stable
     operator fun rem(operand: Float) = Offset(dx % operand, dy % operand)
 
     /**
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt
index 273b101..2648f74 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt
@@ -16,25 +16,32 @@
 
 package androidx.ui.geometry
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 import androidx.ui.util.toStringAsFixed
 import kotlin.math.truncate
 
 /** A radius for either circular or elliptical shapes. */
+@Immutable
 data class Radius(
     /** The radius value on the horizontal axis. */
+    @Stable
     val x: Float,
     /** The radius value on the vertical axis. */
+    @Stable
     val y: Float
 ) {
 
     companion object {
         /** Constructs a circular radius. [x] and [y] will have the same radius value. */
+        @Stable
         fun circular(radius: Float): Radius {
             return Radius(radius, radius)
         }
 
         /** Constructs an elliptical radius with the given radii. */
+        @Stable
         fun elliptical(x: Float, y: Float): Radius {
             return Radius(x, y)
         }
@@ -44,6 +51,7 @@
          *
          * You can use [Radius.zero] with [RRect] to have right-angle corners.
          */
+        @Stable
         val zero: Radius = circular(0.0f)
     }
 
@@ -57,6 +65,7 @@
      * and then adding the result to another radius is equivalent to subtracting
      * a radius of one pixel from the other.
      */
+    @Stable
     operator fun unaryMinus() = elliptical(-x, -y)
 
     /**
@@ -66,6 +75,7 @@
      * minus the right-hand-side operand's [x] and whose [y] value is the
      * left-hand-side operand's [y] minus the right-hand-side operand's [y].
      */
+    @Stable
     operator fun minus(other: Radius) = elliptical(x - other.x, y - other.y)
 
     /**
@@ -75,6 +85,7 @@
      * two operands, and whose [y] value is the sum of the [y] values of the
      * two operands.
      */
+    @Stable
     operator fun plus(other: Radius) = elliptical(x + other.x, y + other.y)
 
     /**
@@ -84,6 +95,7 @@
      * left-hand-side operand (a radius) multiplied by the scalar
      * right-hand-side operand (a Float).
      */
+    @Stable
     operator fun times(operand: Float) = elliptical(x * operand, y * operand)
 
     /**
@@ -93,6 +105,7 @@
      * left-hand-side operand (a radius) divided by the scalar right-hand-side
      * operand (a Float).
      */
+    @Stable
     operator fun div(operand: Float) = elliptical(x / operand, y / operand)
 
     /**
@@ -112,6 +125,7 @@
      * coordinates of the left-hand-side operand (a radius) by the scalar
      * right-hand-side operand (a Float).
      */
+    @Stable
     operator fun rem(operand: Float) = elliptical(x % operand, y % operand)
 
     override fun toString(): String {
@@ -152,6 +166,7 @@
  * Values for [fraction] are usually obtained from an [Animation<Float>], such as
  * an `AnimationController`.
  */
+@Stable
 fun lerp(start: Radius, stop: Radius, fraction: Float): Radius {
     return Radius.elliptical(
         lerp(start.x, stop.x, fraction),
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt
index dbf06c5..9ebaf29 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.geometry
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 import androidx.ui.util.toStringAsFixed
 import kotlin.math.absoluteValue
@@ -34,19 +36,25 @@
  * Rect myRect = const Offset(1.0, 2.0) & const Size(3.0, 4.0);
  * ```
  */
+@Immutable
 data class Rect(
         // The offset of the left edge of this rectangle from the x axis.
+    @Stable
     val left: Float,
         // The offset of the top edge of this rectangle from the y axis.
+    @Stable
     val top: Float,
         // The offset of the right edge of this rectangle from the x axis.
+    @Stable
     val right: Float,
         // The offset of the bottom edge of this rectangle from the y axis.
+    @Stable
     val bottom: Float
 ) {
 
     companion object {
         /** Construct a rectangle from its left, top, right, and bottom edges. */
+        @Stable
         fun fromLTRB(left: Float, top: Float, right: Float, bottom: Float): Rect {
             return Rect(left, top, right, bottom)
         }
@@ -58,6 +66,7 @@
          * To construct a [Rect] from an [Offset] and a [Size], you can use the
          * rectangle constructor operator `&`. See [Offset.&].
          */
+        @Stable
         fun fromLTWH(left: Float, top: Float, width: Float, height: Float): Rect {
             return Rect(left, top, left + width, top + height)
         }
@@ -67,6 +76,7 @@
          *
          * The `center` argument is assumed to be an offset from the origin.
          */
+        @Stable
         fun fromCircle(center: Offset, radius: Float): Rect {
             return Rect(
                 center.dx - radius,
@@ -80,6 +90,7 @@
          * Construct the smallest rectangle that encloses the given offsets, treating
          * them as vectors from the origin.
          */
+        @Stable
         fun fromPoints(a: Offset, b: Offset): Rect {
             return Rect(
                 min(a.dx, b.dx),
@@ -90,6 +101,7 @@
         }
 
         /** A rectangle with left, top, right, and bottom edges all at zero. */
+        @Stable
         val zero: Rect = Rect(0.0f, 0.0f, 0.0f, 0.0f)
 
         val _giantScalar: Float = 1e7f // matches kGiantRect from default_layer_builder.cc
@@ -110,9 +122,11 @@
     }
 
     /** The distance between the left and right edges of this rectangle. */
+    @Stable
     val width = right - left
 
     /** The distance between the top and bottom edges of this rectangle. */
+    @Stable
     val height = bottom - top
 
     // static const int _kDataSize = 4;
@@ -132,6 +146,7 @@
 
     /** Whether any of the coordinates of this rectangle are equal to positive infinity. */
     // included for consistency with Offset and Size
+    @Stable
     fun isInfinite(): Boolean {
         return left >= Float.POSITIVE_INFINITY ||
                 top >= Float.POSITIVE_INFINITY ||
@@ -140,6 +155,7 @@
     }
 
     /** Whether all coordinates of this rectangle are finite. */
+    @Stable
     fun isFinite(): Boolean =
             left.isFinite() &&
             top.isFinite() &&
@@ -150,6 +166,7 @@
      * Whether this rectangle encloses a non-zero area. Negative areas are
      * considered empty.
      */
+    @Stable
     fun isEmpty(): Boolean = left >= right || top >= bottom
 
     /**
@@ -158,6 +175,7 @@
      * To translate a rectangle by separate x and y components rather than by an
      * [Offset], consider [translate].
      */
+    @Stable
     fun shift(offset: Offset): Rect {
         return fromLTRB(left + offset.dx, top + offset.dy, right + offset.dx, bottom + offset.dy)
     }
@@ -169,6 +187,7 @@
      * To translate a rectangle by an [Offset] rather than by separate x and y
      * components, consider [shift].
      */
+    @Stable
     fun translate(translateX: Float, translateY: Float): Rect {
         return fromLTRB(
             left + translateX,
@@ -179,11 +198,13 @@
     }
 
     /** Returns a new rectangle with edges moved outwards by the given delta. */
+    @Stable
     fun inflate(delta: Float): Rect {
         return fromLTRB(left - delta, top - delta, right + delta, bottom + delta)
     }
 
     /** Returns a new rectangle with edges moved inwards by the given delta. */
+    @Stable
     fun deflate(delta: Float): Rect = inflate(-delta)
 
     /**
@@ -192,6 +213,7 @@
      * for this to be meaningful. If the two rectangles do not overlap,
      * then the resulting Rect will have a negative width or height.
      */
+    @Stable
     fun intersect(other: Rect): Rect {
         return fromLTRB(
             max(left, other.left),
@@ -347,6 +369,7 @@
  * Values for [fraction] are usually obtained from an [Animation<Float>], such as
  * an `AnimationController`.
  */
+@Stable
 fun lerp(start: Rect, stop: Rect, fraction: Float): Rect {
     return Rect.fromLTRB(
         lerp(start.left, stop.left, fraction),
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt
index 2001dbf..10ca029 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt
@@ -17,6 +17,7 @@
 package androidx.ui.geometry
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 import androidx.ui.util.packFloats
 import androidx.ui.util.toStringAsFixed
@@ -30,6 +31,7 @@
 /**
  * Constructs a [Size] from the given width and height
  */
+@Stable
 fun Size(width: Float, height: Float) = Size(packFloats(width, height))
 
 /**
@@ -40,9 +42,11 @@
 @Immutable
 inline class Size(@PublishedApi internal val value: Long) {
 
+    @Stable
     val width: Float
         get() = unpackFloat1(value)
 
+    @Stable
     val height: Float
         get() = unpackFloat2(value)
 
@@ -98,6 +102,7 @@
         /**
          * An empty size, one with a zero width and a zero height.
          */
+        @Stable
         val zero = Size(0.0f, 0.0f)
 
         /**
@@ -108,6 +113,7 @@
          *  * [isInfinite], which checks whether either dimension is infinite.
          *  * [isFinite], which checks whether both dimensions are finite.
          */
+        @Stable
         val UnspecifiedSize = Size(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
 
         /**
@@ -125,6 +131,7 @@
          * Values for [fraction] are usually obtained from an [Animation<Float>], such as
          * an `AnimationController`.
          */
+        @Stable
         fun lerp(start: Size, stop: Size, fraction: Float): Size? {
             return Size(
                 lerp(start.width, stop.width, fraction),
@@ -138,6 +145,7 @@
      *
      * Negative areas are considered empty.
      */
+    @Stable
     fun isEmpty() = width <= 0.0f || height <= 0.0f
 
     /**
@@ -158,10 +166,12 @@
      * left-hand-side operand minus the [Offset.dy] dimension of the
      * right-hand-side operand.
      */
+    @Stable
     operator fun minus(other: Offset): Size {
         return Size(width - other.dx, height - other.dy)
     }
 
+    @Stable
     operator fun minus(other: Size): Offset {
         return Offset(width - other.width, height - other.height)
     }
@@ -175,6 +185,7 @@
      * [height] of the left-hand-side operand and the [Offset.dy] dimension of
      * the right-hand-side operand.
      */
+    @Stable
     operator fun plus(other: Offset) = Size(width + other.dx, height + other.dy)
 
     /**
@@ -184,6 +195,7 @@
      * operand (a [Size]) multiplied by the scalar right-hand-side operand (a
      * [Float]).
      */
+    @Stable
     operator fun times(operand: Float) = Size(width * operand, height * operand)
 
     /**
@@ -193,6 +205,7 @@
      * operand (a [Size]) divided by the scalar right-hand-side operand (a
      * [Float]).
      */
+    @Stable
     operator fun div(operand: Float) = Size(width / operand, height / operand)
 
     /**
@@ -202,6 +215,7 @@
      * operand (a [Size]) divided by the scalar right-hand-side operand (a
      * [Float]), rounded towards zero.
      */
+    @Stable
     fun truncDiv(operand: Float) =
         Size(truncate(width / operand), truncate(height / operand))
 
@@ -212,17 +226,20 @@
      * left-hand-side operand (a [Size]) by the scalar right-hand-side operand (a
      * [Float]).
      */
+    @Stable
     operator fun rem(operand: Float) = Size(width % operand, height % operand)
 
     /**
      * The lesser of the magnitudes of the [width] and the [height].
      */
+    @Stable
     val minDimension: Float
         get() = min(width.absoluteValue, height.absoluteValue)
 
     /**
      * The greater of the magnitudes of the [width] and the [height].
      */
+    @Stable
     val maxDimension: Float
         get() = max(width.absoluteValue, height.absoluteValue)
 
@@ -236,6 +253,7 @@
      *
      * See also [Rect.topLeft].
      */
+    @Stable
     fun topLeft(origin: Offset): Offset = origin
 
     /**
@@ -244,6 +262,7 @@
      *
      * See also [Rect.topCenter].
      */
+    @Stable
     fun topCenter(origin: Offset): Offset = Offset(origin.dx + width / 2.0f, origin.dy)
 
     /**
@@ -253,6 +272,7 @@
      *
      * See also [Rect.topRight].
      */
+    @Stable
     fun topRight(origin: Offset): Offset = Offset(origin.dx + width, origin.dy)
 
     /**
@@ -261,6 +281,7 @@
      *
      * See also [Rect.centerLeft].
      */
+    @Stable
     fun centerLeft(origin: Offset): Offset = Offset(origin.dx, origin.dy + height / 2.0f)
 
     /**
@@ -270,6 +291,7 @@
      *
      * See also [Rect.center].
      */
+    @Stable
     fun center(origin: Offset = Offset.zero): Offset = Offset(origin.dx + width / 2.0f, origin.dy +
             height / 2.0f)
 
@@ -279,6 +301,7 @@
      *
      * See also [Rect.centerLeft].
      */
+    @Stable
     fun centerRight(origin: Offset): Offset = Offset(origin.dx + width, origin.dy + height / 2.0f)
 
     /**
@@ -288,6 +311,7 @@
      *
      * See also [Rect.bottomLeft].
      */
+    @Stable
     fun bottomLeft(origin: Offset): Offset = Offset(origin.dx, origin.dy + height)
 
     /**
@@ -297,6 +321,7 @@
      *
      * See also [Rect.bottomLeft].
      */
+    @Stable
     fun bottomCenter(origin: Offset): Offset = Offset(origin.dx + width / 2.0f, origin.dy + height)
 
     /**
@@ -306,6 +331,7 @@
      *
      * See also [Rect.bottomRight].
      */
+    @Stable
     fun bottomRight(origin: Offset): Offset = Offset(origin.dx + width, origin.dy + height)
 
     /**
@@ -316,6 +342,7 @@
      * Rectangles include their top and left edges but exclude their bottom and
      * right edges.
      */
+    @Stable
     fun contains(offset: Offset): Boolean {
         return offset.dx >= 0.0f && offset.dx < width && offset.dy >= 0.0f && offset.dy < height
     }
@@ -323,6 +350,7 @@
     /**
      * A [Size] with the [width] and [height] swapped.
      */
+    @Stable
     fun getFlipped() = Size(height, width)
 
     override fun toString() = "Size(${width.toStringAsFixed(1)}, ${height.toStringAsFixed(1)})"
@@ -331,6 +359,7 @@
 /**
  * Convert a [Size] to a [Rect].
  */
+@Stable
 fun Size.toRect(): Rect {
     return Rect(0f, 0f, width, height)
 }
\ No newline at end of file
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt
index c48ed74..cd839a1 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt
@@ -22,6 +22,7 @@
 import androidx.ui.util.annotation.IntRange
 import androidx.ui.util.annotation.Size
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.graphics.colorspace.ColorModel
 import androidx.ui.graphics.colorspace.ColorSpace
 import androidx.ui.graphics.colorspace.ColorSpaces
@@ -121,6 +122,7 @@
      *
      * @return A non-null instance of [ColorSpace]
      */
+    @Stable
     val colorSpace: ColorSpace
         get() = ColorSpaces.getColorSpace((value and 0x3fUL).toInt())
 
@@ -161,6 +163,7 @@
      * @see blue
      * @see green
      */
+    @Stable
     val red: Float
         get() {
             return if ((value and 0x3fUL) == 0UL) {
@@ -183,6 +186,7 @@
      * @see red
      * @see blue
      */
+    @Stable
     val green: Float
         get() {
             return if ((value and 0x3fUL) == 0UL) {
@@ -205,6 +209,7 @@
      * @see red
      * @see green
      */
+    @Stable
     val blue: Float
         get() {
             return if ((value and 0x3fUL) == 0UL) {
@@ -222,6 +227,7 @@
      * @see green
      * @see blue
      */
+    @Stable
     val alpha: Float
         get() {
             return if ((value and 0x3fUL) == 0UL) {
@@ -231,20 +237,26 @@
             }
         }
 
+    @Stable
     operator fun component1(): Float = red
 
+    @Stable
     operator fun component2(): Float = green
 
+    @Stable
     operator fun component3(): Float = blue
 
+    @Stable
     operator fun component4(): Float = alpha
 
+    @Stable
     operator fun component5(): ColorSpace = colorSpace
 
     /**
      * Copies the existing color, changing only the provided values. The [ColorSpace][colorSpace]
      * of the returned [Color] is the same as this [colorSpace].
      */
+    @Stable
     fun copy(
         alpha: Float = this.alpha,
         red: Float = this.red,
@@ -276,17 +288,29 @@
     }
 
     companion object {
+        @Stable
         val Black = Color(0xFF000000)
+        @Stable
         val DarkGray = Color(0xFF444444)
+        @Stable
         val Gray = Color(0xFF888888)
+        @Stable
         val LightGray = Color(0xFFCCCCCC)
+        @Stable
         val White = Color(0xFFFFFFFF)
+        @Stable
         val Red = Color(0xFFFF0000)
+        @Stable
         val Green = Color(0xFF00FF00)
+        @Stable
         val Blue = Color(0xFF0000FF)
+        @Stable
         val Yellow = Color(0xFFFFFF00)
+        @Stable
         val Cyan = Color(0xFF00FFFF)
+        @Stable
         val Magenta = Color(0xFFFF00FF)
+        @Stable
         val Transparent = Color(0x00000000)
         /**
          * Because Color is an inline class, this represents an unset value
@@ -295,6 +319,7 @@
          * [isUnset] to check for the unset value or [isSet] for any color that isn't
          * [Unset].
          */
+        @Stable
         val Unset = Color(0f, 0f, 0f, 0f, ColorSpaces.Unset)
     }
 }
@@ -305,6 +330,7 @@
  * the default [alpha] is `1.0` (opaque). [colorSpace] must have a [ColorSpace.componentCount] of
  * 3.
  */
+@Stable
 fun Color(
     red: Float,
     green: Float,
@@ -359,6 +385,7 @@
  * @param color The ARGB color int to create a <code>Color</code> from.
  * @return A non-null instance of {@link Color}
  */
+@Stable
 fun Color(@ColorInt color: Int): Color {
     return Color(value = color.toULong() shl 32)
 }
@@ -375,6 +402,7 @@
  * from
  * @return A non-null instance of {@link Color}
  */
+@Stable
 fun Color(color: Long): Color {
     return Color(value = (color.toULong() and 0xffffffffUL) shl 32)
 }
@@ -386,6 +414,7 @@
  *
  * @return A non-null instance of {@link Color}
  */
+@Stable
 fun Color(
     @IntRange(from = 0, to = 0xFF) red: Int,
     @IntRange(from = 0, to = 0xFF) green: Int,
@@ -404,6 +433,7 @@
  * between the two. The [ColorSpace] of the result is always the [ColorSpace][Color.colorSpace]
  * of [stop].
  */
+@Stable
 fun lerp(start: Color, stop: Color, @FloatRange(from = 0.0, to = 1.0) fraction: Float): Color {
     val linearColorSpace = ColorSpaces.LinearExtendedSrgb
     val startColor = start.convert(linearColorSpace)
@@ -440,6 +470,7 @@
  * @return the [Color] representing [this] composited on top of [background], converted to the
  * color space of [background].
  */
+@Stable
 fun Color.compositeOver(background: Color): Color {
     val fg = this.convert(background.colorSpace)
 
@@ -489,6 +520,7 @@
  * @throws IllegalArgumentException If the this color's color space
  * does not use the [RGB][ColorModel.Rgb] color model
  */
+@Stable
 fun Color.luminance(): Float {
     val colorSpace = colorSpace
     require(colorSpace.model === ColorModel.Rgb) {
@@ -515,6 +547,7 @@
  *
  * @return An ARGB color in the sRGB color space
  */
+@Stable
 @ColorInt
 fun Color.toArgb(): Int {
     val colorSpace = colorSpace
@@ -535,11 +568,13 @@
 /**
  * `false` when this is [Color.Unset].
  */
+@Stable
 inline val Color.isSet: Boolean get() = value != Color.Unset.value
 
 /**
  * `true` when this is [Color.Unset].
  */
+@Stable
 inline val Color.isUnset: Boolean get() = value == Color.Unset.value
 
 /**
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt
index 83e14f8..c6e2e64 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt
@@ -16,6 +16,9 @@
 
 package androidx.ui.graphics
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
+
 /**
  * Creates a color filter that applies the blend mode given as the second
  * argument. The source color is the one given as the first argument, and the
@@ -25,14 +28,18 @@
  * to the [Paint.blendMode], using the output of this filter as the source
  * and the background as the destination.
  */
+@Immutable
 data class ColorFilter(
+    @Stable
     val color: Color,
+    @Stable
     val blendMode: BlendMode
 ) {
     companion object {
         /**
          * Helper method to create a [ColorFilter] that tints contents to the specified color
          */
+        @Stable
         fun tint(color: Color): ColorFilter = ColorFilter(color, BlendMode.srcIn)
     }
 }
\ No newline at end of file
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt
index 070c1a2..2a51609 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt
@@ -18,11 +18,13 @@
 
 import androidx.ui.geometry.Size
 import androidx.ui.geometry.toRect
+import androidx.compose.Stable
 import androidx.ui.unit.Density
 
 /**
  * A shape describing the rectangle.
  */
+@Stable
 val RectangleShape: Shape = object : Shape {
     override fun createOutline(size: Size, density: Density) =
         Outline.Rectangle(size.toRect())
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt
index 5e30cb7..ce60cdd 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt
@@ -17,6 +17,7 @@
 package androidx.ui.graphics
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.geometry.Offset
 import androidx.ui.util.lerp
 
@@ -25,14 +26,18 @@
  */
 @Immutable
 data class Shadow(
+    @Stable
     val color: Color = Color(0xFF000000),
+    @Stable
     val offset: Offset = Offset.zero,
+    @Stable
     val blurRadius: Float = 0.0f
 ) {
     companion object {
         /**
          * Constant for no shadow.
          */
+        @Stable
         val None = Shadow()
     }
 }
@@ -40,6 +45,7 @@
 /**
  * Linearly interpolate two [Shadow]s.
  */
+@Stable
 fun lerp(start: Shadow, stop: Shadow, fraction: Float): Shadow {
     return Shadow(
         lerp(start.color, stop.color, fraction),
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt
index 5681095..5394551 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt
@@ -17,11 +17,13 @@
 package androidx.ui.graphics
 
 import androidx.ui.geometry.Size
+import androidx.compose.Immutable
 import androidx.ui.unit.Density
 
 /**
  * Defines a generic shape.
  */
+@Immutable
 interface Shape {
     /**
      * Creates [Outline] of this shape for the given [size].
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt
index 602ff48..a2c0497 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt
@@ -17,6 +17,7 @@
 package androidx.ui.layout
 
 import androidx.compose.Composable
+import androidx.compose.Stable
 
 import androidx.ui.core.AlignmentLine
 import androidx.ui.core.Constraints
@@ -112,6 +113,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.RelativePaddingFromSample
  */
+@Stable
 fun Modifier.relativePaddingFrom(
     alignmentLine: AlignmentLine,
     before: Dp = 0.dp,
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt
index bf55656..47db576 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt
@@ -18,6 +18,8 @@
 
 import androidx.annotation.FloatRange
 import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.core.Alignment
 import androidx.ui.core.Measured
 import androidx.ui.core.Modifier
@@ -79,6 +81,7 @@
  * Scope for the children of [Column].
  */
 @LayoutScopeMarker
+@Immutable
 object ColumnScope {
     /**
      * Position the element horizontally within the [Column] according to [align].
@@ -86,6 +89,7 @@
      * Example usage:
      * @sample androidx.ui.layout.samples.SimpleGravityInColumn
      */
+    @Stable
     fun Modifier.gravity(align: Alignment.Horizontal) = this + GravityModifier(align)
 
     /**
@@ -104,6 +108,7 @@
      * Example usage:
      * @sample androidx.ui.layout.samples.SimpleRelativeToSiblingsInColumn
      */
+    @Stable
     fun Modifier.alignWithSiblings(alignmentLine: VerticalAlignmentLine) =
         this + SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
 
@@ -117,6 +122,7 @@
      *
      * @sample androidx.ui.layout.samples.SimpleColumn
      */
+    @Stable
     fun Modifier.weight(
         @FloatRange(from = 0.0, fromInclusive = false) weight: Float,
         fill: Boolean = true
@@ -142,6 +148,7 @@
      * Example usage:
      * @sample androidx.ui.layout.samples.SimpleRelativeToSiblings
      */
+    @Stable
     fun Modifier.alignWithSiblings(
         alignmentLineBlock: (Measured) -> IntPx
     ) = this + SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt
index 76e6aa5..fcdbe26 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt
@@ -17,6 +17,7 @@
 package androidx.ui.layout
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.core.Constraints
 import androidx.ui.unit.Density
 import androidx.ui.unit.Dp
@@ -30,9 +31,13 @@
  */
 @Immutable
 data class DpConstraints(
+    @Stable
     val minWidth: Dp = 0.dp,
+    @Stable
     val maxWidth: Dp = Dp.Infinity,
+    @Stable
     val minHeight: Dp = 0.dp,
+    @Stable
     val maxHeight: Dp = Dp.Infinity
 ) {
     init {
@@ -58,11 +63,13 @@
         /**
          * Creates constraints tight in both dimensions.
          */
+        @Stable
         fun fixed(width: Dp, height: Dp) = DpConstraints(width, width, height, height)
 
         /**
          * Creates constraints with tight width and loose height.
          */
+        @Stable
         fun fixedWidth(width: Dp) = DpConstraints(
             minWidth = width,
             maxWidth = width,
@@ -73,6 +80,7 @@
         /**
          * Creates constraints with tight height and loose width.
          */
+        @Stable
         fun fixedHeight(height: Dp) = DpConstraints(
             minWidth = 0.dp,
             maxWidth = Dp.Infinity,
@@ -86,38 +94,45 @@
  * Whether or not the upper bound on the maximum height.
  * @see hasBoundedWidth
  */
+@Stable
 val DpConstraints.hasBoundedHeight get() = maxHeight.isFinite()
 
 /**
  * Whether or not the upper bound on the maximum width.
  * @see hasBoundedHeight
  */
+@Stable
 val DpConstraints.hasBoundedWidth get() = maxWidth.isFinite()
 
 /**
  * Whether there is exactly one width value that satisfies the constraints.
  */
+@Stable
 val DpConstraints.hasFixedWidth get() = maxWidth == minWidth
 
 /**
  * Whether there is exactly one height value that satisfies the constraints.
  */
+@Stable
 val DpConstraints.hasFixedHeight get() = maxHeight == minHeight
 
 /**
  * Whether the area of a component respecting these constraints will definitely be 0.
  * This is true when at least one of maxWidth and maxHeight are 0.
  */
+@Stable
 val DpConstraints.isZero get() = maxWidth == 0.dp || maxHeight == 0.dp
 
 /**
  * Whether there is any size that satisfies the current constraints.
  */
+@Stable
 val DpConstraints.satisfiable get() = minWidth <= maxWidth && minHeight <= maxHeight
 
 /**
  * Returns the result of coercing the current constraints in a different set of constraints.
  */
+@Stable
 fun DpConstraints.enforce(otherConstraints: DpConstraints) = DpConstraints(
     minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
     maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
@@ -128,6 +143,7 @@
 /**
  * Returns the DpConstraints obtained by offsetting the current instance with the given values.
  */
+@Stable
 fun DpConstraints.offset(horizontal: Dp = 0.dp, vertical: Dp = 0.dp) = DpConstraints(
     (minWidth + horizontal).coerceAtLeast(0.dp),
     (maxWidth + horizontal).coerceAtLeast(0.dp),
@@ -138,6 +154,7 @@
 /**
  * Creates the [Constraints] corresponding to the current [DpConstraints].
  */
+@Stable
 fun Density.Constraints(dpConstraints: DpConstraints) = Constraints(
     minWidth = dpConstraints.minWidth.toIntPx(),
     maxWidth = dpConstraints.maxWidth.toIntPx(),
@@ -148,6 +165,7 @@
 /**
  * Creates the [DpConstraints] corresponding to the current [Constraints].
  */
+@Stable
 fun Density.DpConstraints(constraints: Constraints) = DpConstraints(
     minWidth = constraints.minWidth.toDp(),
     maxWidth = constraints.maxWidth.toDp(),
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
index 2c6843e..4826d78 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
@@ -17,6 +17,7 @@
 package androidx.ui.layout
 
 import androidx.compose.Composable
+import androidx.compose.Stable
 import androidx.ui.core.Constraints
 import androidx.ui.core.IntrinsicMeasurable
 import androidx.ui.core.IntrinsicMeasureScope
@@ -45,6 +46,7 @@
  * Example usage for max intrinsic:
  * @sample androidx.ui.layout.samples.SameWidthTextBoxes
  */
+@Stable
 fun Modifier.preferredWidth(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {
     IntrinsicSize.Min -> this + PreferredMinIntrinsicWidthModifier
     IntrinsicSize.Max -> this + PreferredMaxIntrinsicWidthModifier
@@ -64,6 +66,7 @@
  * Example usage for max intrinsic:
  * @sample androidx.ui.layout.samples.MatchParentDividerForAspectRatio
  */
+@Stable
 fun Modifier.preferredHeight(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {
     IntrinsicSize.Min -> this + PreferredMinIntrinsicHeightModifier
     IntrinsicSize.Max -> this + PreferredMaxIntrinsicHeightModifier
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt
index c687157..3704381 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt
@@ -17,6 +17,7 @@
 package androidx.ui.layout
 
 import androidx.annotation.FloatRange
+import androidx.compose.Stable
 import androidx.ui.core.Constraints
 import androidx.ui.core.IntrinsicMeasurable
 import androidx.ui.core.IntrinsicMeasureScope
@@ -42,6 +43,7 @@
  *
  * @param ratio the desired width/height positive ratio
  */
+@Stable
 fun Modifier.aspectRatio(
     @FloatRange(from = 0.0, fromInclusive = false)
     ratio: Float
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt
index 9870f58..b135c8e 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt
@@ -16,6 +16,7 @@
 
 package androidx.ui.layout
 
+import androidx.compose.Stable
 import androidx.compose.State
 import androidx.compose.mutableStateOf
 import androidx.ui.core.Constraints
@@ -34,6 +35,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.LayoutOffsetModifier
  */
+@Stable
 fun Modifier.offset(x: Dp = 0.dp, y: Dp = 0.dp) = this + OffsetModifier(x, y)
 
 /**
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt
index 72431f8..b6a7f67 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt
@@ -17,6 +17,7 @@
 package androidx.ui.layout
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.core.Constraints
 import androidx.ui.core.LayoutDirection
 import androidx.ui.core.Modifier
@@ -38,6 +39,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.PaddingModifier
  */
+@Stable
 fun Modifier.padding(
     start: Dp = 0.dp,
     top: Dp = 0.dp,
@@ -62,6 +64,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SymmetricPaddingModifier
  */
+@Stable
 fun Modifier.padding(
     horizontal: Dp = 0.dp,
     vertical: Dp = 0.dp
@@ -83,6 +86,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.PaddingAllModifier
  */
+@Stable
 fun Modifier.padding(all: Dp) =
     this + PaddingModifier(start = all, top = all, end = all, bottom = all, rtlAware = true)
 
@@ -116,6 +120,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.AbsolutePaddingModifier
  */
+@Stable
 fun Modifier.absolutePadding(
     left: Dp = 0.dp,
     top: Dp = 0.dp,
@@ -171,9 +176,13 @@
  */
 @Immutable
 data class InnerPadding(
+    @Stable
     val start: Dp = 0.dp,
+    @Stable
     val top: Dp = 0.dp,
+    @Stable
     val end: Dp = 0.dp,
+    @Stable
     val bottom: Dp = 0.dp
 ) {
     constructor(all: Dp) : this(all, all, all, all)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt
index 9439791..026a18c 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt
@@ -16,6 +16,7 @@
 
 package androidx.ui.layout
 
+import androidx.compose.Stable
 import androidx.ui.core.Alignment
 import androidx.ui.core.Constraints
 import androidx.ui.core.IntrinsicMeasurable
@@ -48,6 +49,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimplePreferredWidthModifier
  */
+@Stable
 fun Modifier.preferredWidth(width: Dp) = preferredSizeIn(minWidth = width, maxWidth = width)
 
 /**
@@ -61,6 +63,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimplePreferredHeightModifier
  */
+@Stable
 fun Modifier.preferredHeight(height: Dp) = preferredSizeIn(minHeight = height, maxHeight = height)
 
 /**
@@ -74,6 +77,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimplePreferredSizeModifier
  */
+@Stable
 fun Modifier.preferredSize(size: Dp) = preferredSizeIn(size, size, size, size)
 
 /**
@@ -88,6 +92,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimplePreferredSizeModifier
  */
+@Stable
 fun Modifier.preferredSize(width: Dp, height: Dp) = preferredSizeIn(
     minWidth = width,
     maxWidth = width,
@@ -101,6 +106,7 @@
  * the requested size will obey the incoming constraints and attempt to be as close as possible
  * to the preferred size.
  */
+@Stable
 fun Modifier.preferredWidthIn(
     minWidth: Dp = Dp.Unspecified,
     maxWidth: Dp = Dp.Unspecified
@@ -112,6 +118,7 @@
  * the requested size will obey the incoming constraints and attempt to be as close as possible
  * to the preferred size.
  */
+@Stable
 fun Modifier.preferredHeightIn(
     minHeight: Dp = Dp.Unspecified,
     maxHeight: Dp = Dp.Unspecified
@@ -123,6 +130,7 @@
  * requested size will obey the incoming constraints and attempt to be as close as possible to
  * the preferred size.
  */
+@Stable
 fun Modifier.preferredSizeIn(constraints: DpConstraints) = preferredSizeIn(
     constraints.minWidth,
     constraints.minHeight,
@@ -136,6 +144,7 @@
  * measurement [Constraints]. If the incoming constraints are more restrictive the requested size
  * will obey the incoming constraints and attempt to be as close as possible to the preferred size.
  */
+@Stable
 fun Modifier.preferredSizeIn(
     minWidth: Dp = Dp.Unspecified,
     minHeight: Dp = Dp.Unspecified,
@@ -158,6 +167,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleWidthModifier
  */
+@Stable
 fun Modifier.width(width: Dp) = sizeIn(minWidth = width, maxWidth = width)
 
 /**
@@ -175,6 +185,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleHeightModifier
  */
+@Stable
 fun Modifier.height(height: Dp) = sizeIn(minHeight = height, maxHeight = height)
 
 /**
@@ -192,6 +203,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleSizeModifier
  */
+@Stable
 fun Modifier.size(size: Dp) = sizeIn(size, size, size, size)
 
 /**
@@ -209,6 +221,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleWidthModifier
  */
+@Stable
 fun Modifier.size(width: Dp, height: Dp) = sizeIn(
     minWidth = width,
     maxWidth = width,
@@ -223,6 +236,7 @@
  * of the content will be automatically offset to be centered on the space assigned to
  * the child by the parent layout under the assumption that [Constraints] were respected.
  */
+@Stable
 fun Modifier.widthIn(
     minWidth: Dp = Dp.Unspecified,
     maxWidth: Dp = Dp.Unspecified
@@ -235,6 +249,7 @@
  * of the content will be automatically offset to be centered on the space assigned to
  * the child by the parent layout under the assumption that [Constraints] were respected.
  */
+@Stable
 fun Modifier.heightIn(
     minHeight: Dp = Dp.Unspecified,
     maxHeight: Dp = Dp.Unspecified
@@ -247,6 +262,7 @@
  * of the content will be automatically offset to be centered on the space assigned to
  * the child by the parent layout under the assumption that [Constraints] were respected.
  */
+@Stable
 fun Modifier.sizeIn(constraints: DpConstraints) = sizeIn(
     constraints.minWidth,
     constraints.minHeight,
@@ -262,6 +278,7 @@
  * of the content will be automatically offset to be centered on the space assigned to
  * the child by the parent layout under the assumption that [Constraints] were respected.
  */
+@Stable
 fun Modifier.sizeIn(
     minWidth: Dp = Dp.Unspecified,
     minHeight: Dp = Dp.Unspecified,
@@ -278,6 +295,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleFillWidthModifier
  */
+@Stable
 fun Modifier.fillMaxWidth() = this + FillModifier(Direction.Horizontal)
 
 /**
@@ -289,6 +307,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleFillHeightModifier
  */
+@Stable
 fun Modifier.fillMaxHeight() = this + FillModifier(Direction.Vertical)
 
 /**
@@ -301,6 +320,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleFillModifier
  */
+@Stable
 fun Modifier.fillMaxSize() = this + FillModifier(Direction.Both)
 
 /**
@@ -311,6 +331,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleWrapContentHorizontallyAlignedModifier
  */
+@Stable
 // TODO(popam): avoid recreating modifier for common align
 fun Modifier.wrapContentWidth(align: Alignment.Horizontal = Alignment.CenterHorizontally) = this +
         AlignmentModifier(Direction.Horizontal) { size, layoutDirection ->
@@ -326,6 +347,7 @@
  * @sample androidx.ui.layout.samples.SimpleWrapContentVerticallyAlignedModifier
  */
 // TODO(popam): avoid recreating modifier for common align
+@Stable
 fun Modifier.wrapContentHeight(align: Alignment.Vertical = Alignment.CenterVertically) =
     this + AlignmentModifier(Direction.Vertical) { size, _ ->
         IntPxPosition(0.ipx, align.align(size.height))
@@ -340,6 +362,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.SimpleWrapContentAlignedModifier
  */
+@Stable
 fun Modifier.wrapContentSize(align: Alignment = Alignment.Center) =
     this + AlignmentModifier(Direction.Both) { size, layoutDirection ->
         align.align(size, layoutDirection)
@@ -355,6 +378,7 @@
  * Example usage:
  * @sample androidx.ui.layout.samples.DefaultMinSizeConstraintsSample
  */
+@Stable
 fun Modifier.defaultMinSizeConstraints(
     minWidth: Dp = Dp.Unspecified,
     minHeight: Dp = Dp.Unspecified
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt
index b58ef9d..c140688 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt
@@ -18,6 +18,8 @@
 
 import androidx.annotation.FloatRange
 import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.core.Alignment
 import androidx.ui.core.HorizontalAlignmentLine
 import androidx.ui.core.Measured
@@ -77,6 +79,7 @@
  * Scope for the children of [Row].
  */
 @LayoutScopeMarker
+@Immutable
 object RowScope {
     /**
      * Position the element vertically within the [Row] according to [align].
@@ -84,6 +87,7 @@
      * Example usage:
      * @sample androidx.ui.layout.samples.SimpleGravityInRow
      */
+    @Stable
     fun Modifier.gravity(align: Alignment.Vertical) = this + GravityModifier(align)
 
     /**
@@ -104,6 +108,7 @@
      * Example usage:
      * @sample androidx.ui.layout.samples.SimpleRelativeToSiblingsInRow
      */
+    @Stable
     fun Modifier.alignWithSiblings(alignmentLine: HorizontalAlignmentLine) =
         this + SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
 
@@ -115,6 +120,7 @@
      * Otherwise, the element is allowed to be smaller - this will result in [Row] being smaller,
      * as the unused allocated width will not be redistributed to other siblings.
      */
+    @Stable
     fun Modifier.weight(
         @FloatRange(from = 0.0, fromInclusive = false) weight: Float,
         fill: Boolean = true
@@ -140,6 +146,7 @@
      * Example usage:
      * @sample androidx.ui.layout.samples.SimpleRelativeToSiblings
      */
+    @Stable
     fun Modifier.alignWithSiblings(
         alignmentLineBlock: (Measured) -> IntPx
     ) = this + SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt
index 3e0beef..153fa40 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.Composable
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.core.Alignment
 import androidx.ui.core.AlignmentLine
 import androidx.ui.core.Constraints
@@ -558,6 +559,7 @@
  * Used to specify the alignment of a layout's children, in cross axis direction.
  */
 // TODO(popam): refine this API surface with modifiers - add type safety for alignment orientation.
+@Immutable
 class CrossAxisAlignment private constructor(
     internal val alignmentLineProvider: AlignmentLineProvider? = null
 ) {
@@ -565,16 +567,19 @@
         /**
          * Place children such that their center is in the middle of the cross axis.
          */
+        @Stable
         val Center = CrossAxisAlignment(null)
         /**
          * Place children such that their start edge is aligned to the start edge of the cross
          * axis. TODO(popam): Consider rtl directionality.
          */
+        @Stable
         val Start = CrossAxisAlignment(null)
         /**
          * Place children such that their end edge is aligned to the end edge of the cross
          * axis. TODO(popam): Consider rtl directionality.
          */
+        @Stable
         val End = CrossAxisAlignment(null)
         /**
          * Align children by their baseline.
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
index 8420164..b20a19e 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
@@ -17,6 +17,8 @@
 package androidx.ui.layout
 
 import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.core.Alignment
 import androidx.ui.core.Constraints
 import androidx.ui.core.Layout
@@ -97,10 +99,12 @@
  * A StackScope provides a scope for the children of a [Stack].
  */
 @LayoutScopeMarker
+@Immutable
 class StackScope {
     /**
      * Pull the content element to a specific [Alignment] within the [Stack].
      */
+    @Stable
     fun Modifier.gravity(align: Alignment) = this + StackGravityModifier(align)
 
     /**
@@ -115,9 +119,11 @@
      * using it for an element inside a [Stack] will make the [Stack] itself always fill the
      * available space.
      */
+    @Stable
     fun Modifier.matchParentSize() = this + StretchGravityModifier
 
     internal companion object {
+        @Stable
         val StretchGravityModifier: ParentDataModifier =
             StackGravityModifier(Alignment.Center, true)
     }
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt b/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt
index 3b87230..4bf2a4d 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt
@@ -16,6 +16,7 @@
 
 package androidx.ui.input
 
+import androidx.compose.Immutable
 import androidx.ui.text.TextRange
 import androidx.ui.text.substring
 import kotlin.math.max
@@ -28,6 +29,7 @@
  * This class stores a snapshot of the input state of the edit buffer and provide utility functions
  * for answering these information retrieval requests.
  */
+@Immutable
 data class EditorValue(
     /**
      * A text visible to IME
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt b/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt
index 683e4a32..69455f2 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.input
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.text.AnnotatedString
 
 /**
@@ -80,6 +82,7 @@
  * For example, you can mask characters in password filed with asterisk with
  * PasswordVisualTransformation.
  */
+@Immutable
 interface VisualTransformation {
     /**
      * Change the visual output of given text.
@@ -109,6 +112,7 @@
         /**
          * A special visual transformation object indicating that no transformation is applied.
          */
+        @Stable
         val None: VisualTransformation = object : VisualTransformation {
             override fun filter(text: AnnotatedString) =
                 TransformedText(text, OffsetMap.identityOffsetMap)
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt
index abc9758..b58d03f 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.text
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.text.AnnotatedString.Builder
 import androidx.ui.text.AnnotatedString.Range
 import androidx.ui.util.fastForEach
@@ -39,6 +41,7 @@
  * The basic data structure of text with multiple styles. To construct an [AnnotatedString] you
  * can use [Builder].
  */
+@Immutable
 data class AnnotatedString internal constructor(
     val text: String,
     val spanStyles: List<SpanStyleRange> = listOf(),
@@ -83,6 +86,7 @@
         }
     }
 
+    @Stable
     operator fun plus(other: AnnotatedString): AnnotatedString {
         return with(Builder(this)) {
             append(other)
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt
index f1556c5..acf74b7 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt
@@ -17,6 +17,7 @@
 package androidx.ui.text
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.text.style.TextAlign
 import androidx.ui.text.style.TextDirectionAlgorithm
 import androidx.ui.text.style.TextIndent
@@ -66,6 +67,7 @@
      *
      * If the given paragraph style is null, returns this paragraph style.
      */
+    @Stable
     fun merge(other: ParagraphStyle? = null): ParagraphStyle {
         if (other == null) return this
 
@@ -84,6 +86,7 @@
     /**
      * Plus operator overload that applies a [merge].
      */
+    @Stable
     operator fun plus(other: ParagraphStyle): ParagraphStyle = this.merge(other)
 }
 
@@ -100,6 +103,7 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
+@Stable
 fun lerp(start: ParagraphStyle, stop: ParagraphStyle, fraction: Float): ParagraphStyle {
     return ParagraphStyle(
         textAlign = lerpDiscrete(start.textAlign, stop.textAlign, fraction),
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt
index d922d70..d778c0b 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt
@@ -16,6 +16,7 @@
 
 package androidx.ui.text
 
+import androidx.compose.Immutable
 import androidx.ui.unit.TextUnit
 
 /**
@@ -31,6 +32,7 @@
  * Check [PlaceholderVerticalAlign] for more information.
  * @throws IllegalArgumentException if [TextUnit.Inherit] is passed to [width] or [height].
  */
+@Immutable
 data class Placeholder(
     val width: TextUnit,
     val height: TextUnit,
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt
index 0737a49..7f6bc55 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt
@@ -17,6 +17,7 @@
 package androidx.ui.text
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.Shadow
 import androidx.ui.graphics.lerp
@@ -91,6 +92,7 @@
      *
      * If the given span style is null, returns this span style.
      */
+    @Stable
     fun merge(other: SpanStyle? = null): SpanStyle {
         if (other == null) return this
 
@@ -119,6 +121,7 @@
     /**
      * Plus operator overload that applies a [merge].
      */
+    @Stable
     operator fun plus(other: SpanStyle): SpanStyle = this.merge(other)
 }
 
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt
index c5035ed..4fa69a7 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.text
 
+import androidx.compose.Immutable
+
 fun CharSequence.substring(range: TextRange): String = this.substring(range.min, range.max)
 
 /**
@@ -26,6 +28,7 @@
  * @param start the inclusive start offset of the range.
  * @param end the exclusive end offset of the range
  */
+@Immutable
 data class TextRange(val start: Int, val end: Int) {
     /** The minimum offset of the range. */
     val min: Int get() = if (start > end) end else start
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt
index 4fb7c2e..7bd0d66 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt
@@ -18,6 +18,7 @@
 
 import androidx.annotation.VisibleForTesting
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.core.LayoutDirection
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.Shadow
@@ -128,6 +129,7 @@
         }
     }
 
+    @Stable
     fun toSpanStyle(): SpanStyle = SpanStyle(
         color = color,
         fontSize = fontSize,
@@ -145,6 +147,7 @@
         shadow = shadow
     )
 
+    @Stable
     fun toParagraphStyle(): ParagraphStyle = ParagraphStyle(
         textAlign = textAlign,
         textDirectionAlgorithm = textDirectionAlgorithm,
@@ -161,6 +164,7 @@
      *
      * If the given text style is null, returns this text style.
      */
+    @Stable
     fun merge(other: TextStyle? = null): TextStyle {
         if (other == null || other == Default) return this
         return TextStyle(
@@ -174,6 +178,7 @@
      *
      * @see merge
      */
+    @Stable
     fun merge(other: SpanStyle): TextStyle {
         return TextStyle(
             spanStyle = toSpanStyle().merge(other),
@@ -186,6 +191,7 @@
      *
      * @see merge
      */
+    @Stable
     fun merge(other: ParagraphStyle): TextStyle {
         return TextStyle(
             spanStyle = toSpanStyle(),
@@ -196,22 +202,26 @@
     /**
      * Plus operator overload that applies a [merge].
      */
+    @Stable
     operator fun plus(other: TextStyle): TextStyle = this.merge(other)
 
     /**
      * Plus operator overload that applies a [merge].
      */
+    @Stable
     operator fun plus(other: ParagraphStyle): TextStyle = this.merge(other)
 
     /**
      * Plus operator overload that applies a [merge].
      */
+    @Stable
     operator fun plus(other: SpanStyle): TextStyle = this.merge(other)
 
     companion object {
         /**
          * Constant for default text style.
          */
+        @Stable
         val Default = TextStyle()
     }
 }
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt
index ce6dc80..dbb7568 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt
@@ -16,11 +16,15 @@
 
 package androidx.ui.text.font
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
+
 /**
  * The interface of the font resource.
  *
  * @see ResourceFont
  */
+@Immutable
 interface Font {
     /**
      * The weight of the font. The system uses this to match a font to a font request
@@ -78,6 +82,7 @@
  *
  * @see FontFamily
  */
+@Stable
 fun font(
     resId: Int,
     weight: FontWeight = FontWeight.Normal,
@@ -87,4 +92,5 @@
 /**
  * Create a [FontFamily] from this single [font].
  */
+@Stable
 fun Font.asFontFamily() = fontFamily(this)
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt
index eadbbf5..1a9a765 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt
@@ -17,6 +17,7 @@
 package androidx.ui.text.font
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.text.Typeface
 
 /**
@@ -106,6 +107,7 @@
  *
  * @param fonts list of font files
  */
+@Stable
 fun fontFamily(fonts: List<Font>) = FontListFontFamily(fonts)
 
 /**
@@ -113,6 +115,7 @@
  *
  * @param fonts list of font files
  */
+@Stable
 fun fontFamily(vararg fonts: Font) = FontListFontFamily(fonts.asList())
 
 /**
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt
index f3c6e12..72bce00 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt
@@ -16,6 +16,7 @@
 package androidx.ui.text.font
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 
 /**
@@ -37,43 +38,61 @@
 
     companion object {
         /** [Thin] */
+        @Stable
         val W100 = FontWeight(100)
         /** [ExtraLight] */
+        @Stable
         val W200 = FontWeight(200)
         /** [Light] */
+        @Stable
         val W300 = FontWeight(300)
         /** [Normal] / regular / plain */
+        @Stable
         val W400 = FontWeight(400)
         /** [Medium] */
+        @Stable
         val W500 = FontWeight(500)
         /** [SemiBold] */
+        @Stable
         val W600 = FontWeight(600)
         /** [Bold] */
+        @Stable
         val W700 = FontWeight(700)
         /** [ExtraBold] */
+        @Stable
         val W800 = FontWeight(800)
         /** [Black] */
+        @Stable
         val W900 = FontWeight(900)
 
         /** Alias for [W100] */
+        @Stable
         val Thin = W100
         /** Alias for [W200] */
+        @Stable
         val ExtraLight = W200
         /** Alias for [W300] */
+        @Stable
         val Light = W300
         /** The default font weight - alias for [W400] */
+        @Stable
         val Normal = W400
         /** Alias for [W500] */
+        @Stable
         val Medium = W500
         /** Alias for [W600] */
+        @Stable
         val SemiBold = W600
         /**
          * A commonly used font weight that is heavier than normal - alias for [W700]
          */
+        @Stable
         val Bold = W700
         /** Alias for [W800] */
+        @Stable
         val ExtraBold = W800
         /** Alias for [W900] */
+        @Stable
         val Black = W900
 
         /** A list of all the font weights. */
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt
index b36cda5..d6d4cb3 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt
@@ -17,6 +17,7 @@
 package androidx.ui.text.style
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 
 /**
@@ -34,16 +35,19 @@
         /**
          * Default baseline shift for superscript.
          */
+        @Stable
         val Superscript = BaselineShift(0.5f)
 
         /**
          * Default baseline shift for subscript
          */
+        @Stable
         val Subscript = BaselineShift(-0.5f)
 
         /**
          * Constant for no baseline shift.
          */
+        @Stable
         val None = BaselineShift(0.0f)
     }
 }
@@ -51,6 +55,7 @@
 /**
  * Linearly interpolate two [BaselineShift]s.
  */
+@Stable
 fun lerp(start: BaselineShift, stop: BaselineShift, fraction: Float): BaselineShift {
     return BaselineShift(
         lerp(
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt
index 7a5a2ba..536945d 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt
@@ -16,6 +16,7 @@
 package androidx.ui.text.style
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 
 /**
  * Defines a horizontal line to be drawn on the text.
@@ -24,6 +25,7 @@
 data class TextDecoration internal constructor(val mask: Int) {
 
     companion object {
+        @Stable
         val None: TextDecoration = TextDecoration(0x0)
 
         /**
@@ -31,6 +33,7 @@
          *
          * @sample androidx.ui.text.samples.TextDecorationUnderlineSample
          */
+        @Stable
         val Underline: TextDecoration = TextDecoration(0x1)
 
         /**
@@ -38,6 +41,7 @@
          *
          * @sample androidx.ui.text.samples.TextDecorationLineThroughSample
          */
+        @Stable
         val LineThrough: TextDecoration = TextDecoration(0x2)
 
         /**
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt
index 1c35eb9..2acfb4c 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt
@@ -17,6 +17,7 @@
 package androidx.ui.text.style
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 
 /**
@@ -34,6 +35,7 @@
     val skewX: Float = 0f
 ) {
     companion object {
+        @Stable
         internal val None = TextGeometricTransform(1.0f, 0.0f)
     }
 }
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt
index c5c3e73..d54b399 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt
@@ -17,6 +17,7 @@
 package androidx.ui.text.style
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.TextUnit
 import androidx.ui.unit.sp
 import androidx.ui.text.lerpTextUnitInheritable
@@ -36,6 +37,7 @@
         /**
          * Constant fot no text indent.
          */
+        @Stable
         val None = TextIndent()
     }
 }
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt
index 5eeb2da..d7439d3 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt
@@ -17,6 +17,7 @@
 package androidx.ui.core
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.IntPx
 import androidx.ui.unit.IntPxSize
 import androidx.ui.unit.coerceAtLeast
@@ -58,9 +59,13 @@
  */
 @Immutable
 data class Constraints(
+    @Stable
     val minWidth: IntPx = IntPx.Zero,
+    @Stable
     val maxWidth: IntPx = IntPx.Infinity,
+    @Stable
     val minHeight: IntPx = IntPx.Zero,
+    @Stable
     val maxHeight: IntPx = IntPx.Infinity
 ) {
     init {
@@ -83,11 +88,13 @@
         /**
          * Creates constraints for fixed size in both dimensions.
          */
+        @Stable
         fun fixed(width: IntPx, height: IntPx) = Constraints(width, width, height, height)
 
         /**
          * Creates constraints for fixed width and unspecified height.
          */
+        @Stable
         fun fixedWidth(width: IntPx) = Constraints(
             minWidth = width,
             maxWidth = width,
@@ -98,6 +105,7 @@
         /**
          * Creates constraints for fixed height and unspecified width.
          */
+        @Stable
         fun fixedHeight(height: IntPx) = Constraints(
             minWidth = IntPx.Zero,
             maxWidth = IntPx.Infinity,
@@ -111,33 +119,39 @@
  * Whether or not the upper bound on the maximum height.
  * @see hasBoundedWidth
  */
+@Stable
 val Constraints.hasBoundedHeight get() = maxHeight.isFinite()
 
 /**
  * Whether or not the upper bound on the maximum width.
  * @see hasBoundedHeight
  */
+@Stable
 val Constraints.hasBoundedWidth get() = maxWidth.isFinite()
 
 /**
  * Whether there is exactly one width value that satisfies the constraints.
  */
+@Stable
 val Constraints.hasFixedWidth get() = maxWidth == minWidth
 
 /**
  * Whether there is exactly one height value that satisfies the constraints.
  */
+@Stable
 val Constraints.hasFixedHeight get() = maxHeight == minHeight
 
 /**
  * Whether the area of a component respecting these constraints will definitely be 0.
  * This is true when at least one of maxWidth and maxHeight are 0.
  */
+@Stable
 val Constraints.isZero get() = maxWidth == IntPx.Zero || maxHeight == IntPx.Zero
 
 /**
  * Returns the result of coercing the current constraints in a different set of constraints.
  */
+@Stable
 fun Constraints.enforce(otherConstraints: Constraints) = Constraints(
     minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
     maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
@@ -148,6 +162,7 @@
 /**
  * Takes a size and returns the closest size to it that satisfies the constraints.
  */
+@Stable
 fun Constraints.constrain(size: IntPxSize) = IntPxSize(
     size.width.coerceIn(minWidth, maxWidth),
     size.height.coerceIn(minHeight, maxHeight)
@@ -156,6 +171,7 @@
 /**
  * Takes a size and returns whether it satisfies the current constraints.
  */
+@Stable
 fun Constraints.satisfiedBy(size: IntPxSize) =
         minWidth <= size.width && size.width <= maxWidth &&
                 minHeight <= size.height && size.height <= maxHeight
@@ -163,6 +179,7 @@
 /**
  * Returns the Constraints obtained by offsetting the current instance with the given values.
  */
+@Stable
 fun Constraints.offset(horizontal: IntPx = 0.ipx, vertical: IntPx = 0.ipx) = Constraints(
     (minWidth + horizontal).coerceAtLeast(0.ipx),
     (maxWidth + horizontal).coerceAtLeast(0.ipx),
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt
index e767779..c979e1b 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt
@@ -18,6 +18,7 @@
 package androidx.ui.core
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.IntSize
 import androidx.ui.util.annotation.IntRange
 import kotlin.math.max
@@ -70,12 +71,14 @@
      * * MinFocusHeight
      * * MaxFocusHeight
      */
+    @Stable
     private val focusIndex
         get() = (value and FocusMask).toInt()
 
     /**
      * The minimum width that the measurement can take.
      */
+    @Stable
     val minWidth: Int
         @IntRange(from = 0)
         get() {
@@ -87,6 +90,7 @@
      * The maximum width that the measurement can take. This will either be
      * a positive value greater than or equal to [minWidth] or [Constraints2.Infinity].
      */
+    @Stable
     val maxWidth: Int
         get() {
             val mask = WidthMask[focusIndex]
@@ -97,6 +101,7 @@
     /**
      * The minimum height that the measurement can take.
      */
+    @Stable
     val minHeight: Int
         @IntRange(from = 0)
         get() {
@@ -110,6 +115,7 @@
      * The maximum height that the measurement can take. This will either be
      * a positive value greater than or equal to [minHeight] or [Constraints2.Infinity].
      */
+    @Stable
     val maxHeight: Int
         get() {
             val focus = focusIndex
@@ -123,6 +129,7 @@
      * Whether or not the upper bound on the maximum width.
      * @see hasBoundedHeight
      */
+    @Stable
     val hasBoundedWidth: Boolean
         get() {
             val mask = WidthMask[focusIndex]
@@ -133,6 +140,7 @@
      * Whether or not the upper bound on the maximum height.
      * @see hasBoundedWidth
      */
+    @Stable
     val hasBoundedHeight: Boolean
         get() {
             val focus = focusIndex
@@ -147,6 +155,7 @@
      * [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],
      * respectively, or [Infinity].
      */
+    @Stable
     fun copy(
         minWidth: Int = this.minWidth,
         maxWidth: Int = this.maxWidth,
@@ -174,6 +183,7 @@
          * be considered infinite. [hasBoundedHeight] or [hasBoundedWidth] will be
          * `true` when [maxHeight] or [maxWidth] is [Infinity], respectively.
          */
+        @Stable
         const val Infinity = Int.MIN_VALUE / 2
 
         /**
@@ -289,6 +299,7 @@
         /**
          * Creates constraints for fixed size in both dimensions.
          */
+        @Stable
         fun fixed(width: Int, height: Int): Constraints2 {
             require(width >= 0 && height >= 0) {
                 "width($width) and height($height) must be >= 0"
@@ -299,6 +310,7 @@
         /**
          * Creates constraints for fixed width and unspecified height.
          */
+        @Stable
         fun fixedWidth(width: Int): Constraints2 {
             require(width >= 0) {
                 "width($width) must be >= 0"
@@ -314,6 +326,7 @@
         /**
          * Creates constraints for fixed height and unspecified width.
          */
+        @Stable
         fun fixedHeight(height: Int): Constraints2 {
             require(height >= 0) {
                 "height($height) must be >= 0"
@@ -390,6 +403,7 @@
  * [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],
  * respectively, or [Infinity][Constraints2.Infinity].
  */
+@Stable
 fun Constraints2(
     @IntRange(from = 0)
     minWidth: Int = 0,
@@ -413,22 +427,26 @@
 /**
  * Whether there is exactly one width value that satisfies the constraints.
  */
+@Stable
 val Constraints2.hasFixedWidth get() = maxWidth == minWidth
 
 /**
  * Whether there is exactly one height value that satisfies the constraints.
  */
+@Stable
 val Constraints2.hasFixedHeight get() = maxHeight == minHeight
 
 /**
  * Whether the area of a component respecting these constraints will definitely be 0.
  * This is true when at least one of maxWidth and maxHeight are 0.
  */
+@Stable
 val Constraints2.isZero get() = maxWidth == 0 || maxHeight == 0
 
 /**
  * Returns the result of coercing the current constraints in a different set of constraints.
  */
+@Stable
 fun Constraints2.enforce(otherConstraints: Constraints2) = Constraints2(
     minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
     maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
@@ -439,6 +457,7 @@
 /**
  * Takes a size and returns the closest size to it that satisfies the constraints.
  */
+@Stable
 fun Constraints2.constrain(size: IntSize) = IntSize(
     width = size.width.coerceIn(minWidth, maxWidth),
     height = size.height.coerceIn(minHeight, maxHeight)
@@ -447,12 +466,14 @@
 /**
  * Takes a size and returns whether it satisfies the current constraints.
  */
+@Stable
 fun Constraints2.satisfiedBy(size: IntSize) =
     size.width in minWidth..maxWidth && size.height in minHeight..maxHeight
 
 /**
  * Returns the Constraints obtained by offsetting the current instance with the given values.
  */
+@Stable
 fun Constraints2.offset(horizontal: Int = 0, vertical: Int = 0) = Constraints2(
     (minWidth + horizontal).coerceAtLeast(0),
     (maxWidth + horizontal).coerceAtLeast(0),
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt
index 8ecebb7..47c8601 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt
@@ -16,6 +16,8 @@
 
 package androidx.ui.unit
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.geometry.Rect
 import kotlin.math.roundToInt
 
@@ -25,6 +27,7 @@
  * @param density The logical density of the display. This is a scaling factor for the [Dp] unit.
  * @param fontScale Current user preference for the scaling factor for fonts.
  */
+@Stable
 fun Density(density: Float, fontScale: Float = 1f): Density =
     DensityImpl(density, fontScale)
 
@@ -38,37 +41,44 @@
  *
  * @sample androidx.ui.unit.samples.WithDensitySample
  */
+@Immutable
 interface Density {
 
     /**
      * The logical density of the display. This is a scaling factor for the [Dp] unit.
      */
+    @Stable
     val density: Float
 
     /**
      * Current user preference for the scaling factor for fonts.
      */
+    @Stable
     val fontScale: Float
 
     /**
      * Convert [Dp] to pixels. Pixels are used to paint to Canvas.
      */
+    @Stable
     fun Dp.toPx(): Float = value * density
 
     /**
      * Convert [Dp] to [IntPx] by rounding
      */
+    @Stable
     fun Dp.toIntPx(): IntPx = toPx().roundToInt().ipx
 
     /**
      * Convert [Dp] to Sp. Sp is used for font size, etc.
      */
+    @Stable
     fun Dp.toSp(): TextUnit = TextUnit.Sp(value / fontScale)
 
     /**
      * Convert Sp to pixels. Pixels are used to paint to Canvas.
      * @throws IllegalStateException if TextUnit other than SP unit is specified.
      */
+    @Stable
     fun TextUnit.toPx(): Float {
         check(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
         return value * fontScale * density
@@ -77,12 +87,14 @@
     /**
      * Convert Sp to [IntPx] by rounding
      */
+    @Stable
     fun TextUnit.toIntPx(): IntPx = toPx().roundToInt().ipx
 
     /**
      * Convert Sp to [Dp].
      * @throws IllegalStateException if TextUnit other than SP unit is specified.
      */
+    @Stable
     fun TextUnit.toDp(): Dp {
         check(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
         return Dp(value * fontScale)
@@ -91,38 +103,47 @@
     /**
      * Convert [Px] to [Dp].
      */
+    @Stable
     fun Px.toDp(): Dp = (value / density).dp
 
     /**
      * Convert [Px] to Sp.
      */
+    @Stable
     fun Px.toSp(): TextUnit = (value / (fontScale * density)).sp
 
     /**
      * Convert [IntPx] to [Dp].
      */
+    @Stable
     fun IntPx.toDp(): Dp = (value / density).dp
 
     /**
      * Convert [IntPx] to Sp.
      */
+    @Stable
     fun IntPx.toSp(): TextUnit = (value / (fontScale * density)).sp
 
     /** Convert a [Float] pixel value to a Dp */
+    @Stable
     fun Float.toDp(): Dp = (this / density).dp
 
     /** Convert a [Float] pixel value to a Sp */
+    @Stable
     fun Float.toSp(): TextUnit = (this / (fontScale * density)).sp
 
     /** Convert a [Int] pixel value to a Dp */
+    @Stable
     fun Int.toDp(): Dp = toFloat().toDp()
 
     /** Convert a [Int] pixel value to a Sp */
+    @Stable
     fun Int.toSp(): TextUnit = toFloat().toSp()
 
     /**
      * Convert a [Bounds] to a [Rect].
      */
+    @Stable
     fun Bounds.toRect(): Rect {
         return Rect(
             left.toPx(),
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt
index 0184f2b..67bb906 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt
@@ -18,6 +18,7 @@
 package androidx.ui.unit
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.Dp.Companion.Hairline
 import androidx.ui.util.lerp
 import androidx.ui.util.packFloats
@@ -46,66 +47,79 @@
     /**
      * Add two [Dp]s together.
      */
+    @Stable
     inline operator fun plus(other: Dp) =
         Dp(value = this.value + other.value)
 
     /**
      * Subtract a Dp from another one.
      */
+    @Stable
     inline operator fun minus(other: Dp) =
         Dp(value = this.value - other.value)
 
     /**
      * This is the same as multiplying the Dp by -1.0.
      */
+    @Stable
     inline operator fun unaryMinus() = Dp(-value)
 
     /**
      * Divide a Dp by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): Dp =
         Dp(value = value / other)
 
+    @Stable
     inline operator fun div(other: Int): Dp =
         Dp(value = value / other)
 
     /**
      * Divide by another Dp to get a scalar.
      */
+    @Stable
     inline operator fun div(other: Dp): Float = value / other.value
 
     /**
      * Divide by [DpSquared] to get a [DpInverse].
      */
+    @Stable
     inline operator fun div(other: DpSquared): DpInverse =
         DpInverse(value = value / other.value)
 
     /**
      * Multiply a Dp by a scalar.
      */
+    @Stable
     inline operator fun times(other: Float): Dp =
         Dp(value = value * other)
 
+    @Stable
     inline operator fun times(other: Int): Dp =
         Dp(value = value * other)
 
     /**
      * Multiply by a Dp to get a [DpSquared] result.
      */
+    @Stable
     inline operator fun times(other: Dp): DpSquared =
         DpSquared(value = value * other.value)
 
     /**
      * Multiply by a Dp to get a [DpSquared] result.
      */
+    @Stable
     inline operator fun times(other: DpSquared): DpCubed =
         DpCubed(value = value * other.value)
 
     /**
      * Support comparing Dimensions with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: Dp) = value.compareTo(other.value)
 
+    @Stable
     override fun toString() = "$value.dp"
 
     companion object {
@@ -113,16 +127,19 @@
          * A dimension used to represent a hairline drawing element. Hairline elements take up no
          * space, but will draw a single pixel, independent of the device's resolution and density.
          */
+        @Stable
         val Hairline = Dp(value = 0f)
 
         /**
          * Infinite dp dimension.
          */
+        @Stable
         val Infinity = Dp(value = Float.POSITIVE_INFINITY)
 
         /**
          * Constant that means unspecified Dp
          */
+        @Stable
         val Unspecified = Dp(value = Float.NaN)
     }
 }
@@ -134,7 +151,8 @@
  *     // -- or --
  *     val y = 10.dp
  */
-inline val Int.dp: Dp get() = if (this == 0) Hairline else Dp(value = this.toFloat())
+@Stable
+inline val Int.dp: Dp get() = Dp(value = this.toFloat())
 
 /**
  * Create a [Dp] using a [Double]:
@@ -143,7 +161,8 @@
  *     // -- or --
  *     val y = 10.0.dp
  */
-inline val Double.dp: Dp get() = if (this == 0.0) Hairline else Dp(value = this.toFloat())
+@Stable
+inline val Double.dp: Dp get() = Dp(value = this.toFloat())
 
 /**
  * Create a [Dp] using a [Float]:
@@ -152,28 +171,37 @@
  *     // -- or --
  *     val y = 10f.dp
  */
-inline val Float.dp: Dp get() = if (this == 0f) Hairline else Dp(value = this)
+@Stable
+inline val Float.dp: Dp get() = Dp(value = this)
 
+@Stable
 inline operator fun Float.div(other: Dp) =
     DpInverse(this / other.value)
 
+@Stable
 inline operator fun Double.div(other: Dp) =
     DpInverse(this.toFloat() / other.value)
 
+@Stable
 inline operator fun Int.div(other: Dp) =
     DpInverse(this / other.value)
 
+@Stable
 inline operator fun Float.times(other: Dp) =
     Dp(this * other.value)
 
+@Stable
 inline operator fun Double.times(other: Dp) =
     Dp(this.toFloat() * other.value)
 
+@Stable
 inline operator fun Int.times(other: Dp) =
     Dp(this * other.value)
 
+@Stable
 inline fun min(a: Dp, b: Dp): Dp = Dp(value = min(a.value, b.value))
 
+@Stable
 inline fun max(a: Dp, b: Dp): Dp = Dp(value = max(a.value, b.value))
 
 /**
@@ -182,6 +210,7 @@
  * @return this value if it's in the range, or [minimumValue] if this value is less than
  * [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
  */
+@Stable
 inline fun Dp.coerceIn(minimumValue: Dp, maximumValue: Dp): Dp =
     Dp(value = value.coerceIn(minimumValue.value, maximumValue.value))
 
@@ -190,6 +219,7 @@
  * @return this value if it's greater than or equal to the [minimumValue] or the
  * [minimumValue] otherwise.
  */
+@Stable
 inline fun Dp.coerceAtLeast(minimumValue: Dp): Dp =
     Dp(value = value.coerceAtLeast(minimumValue.value))
 
@@ -199,6 +229,7 @@
  * @return this value if it's less than or equal to the [maximumValue] or the
  * [maximumValue] otherwise.
  */
+@Stable
 inline fun Dp.coerceAtMost(maximumValue: Dp): Dp =
     Dp(value = value.coerceAtMost(maximumValue.value))
 
@@ -206,6 +237,7 @@
  *
  * Return `true` when it is finite or `false` when it is [Dp.Infinity]
  */
+@Stable
 inline fun Dp.isFinite(): Boolean = value != Float.POSITIVE_INFINITY
 
 /**
@@ -219,6 +251,7 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
+@Stable
 fun lerp(start: Dp, stop: Dp, fraction: Float): Dp {
     return Dp(lerp(start.value, stop.value, fraction))
 }
@@ -238,56 +271,66 @@
     /**
      * Add two DimensionSquares together.
      */
+    @Stable
     inline operator fun plus(other: DpSquared) =
         DpSquared(value = value + other.value)
 
     /**
      * Subtract a DimensionSquare from another one.
      */
+    @Stable
     inline operator fun minus(other: DpSquared) =
         DpSquared(value = value - other.value)
 
     /**
      * Divide a DimensionSquare by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): DpSquared =
         DpSquared(value = value / other)
 
     /**
      * Divide by a [Dp] to get a [Dp] result.
      */
+    @Stable
     inline operator fun div(other: Dp): Dp =
         Dp(value = value / other.value)
 
     /**
      * Divide by a DpSquared to get a scalar result.
      */
+    @Stable
     inline operator fun div(other: DpSquared): Float = value / other.value
 
     /**
      * Divide by a [DpCubed] to get a [DpInverse] result.
      */
+    @Stable
     inline operator fun div(other: DpCubed): DpInverse =
         DpInverse(value / other.value)
 
     /**
      * Multiply by a scalar to get a DpSquared result.
      */
+    @Stable
     inline operator fun times(other: Float): DpSquared =
         DpSquared(value = value * other)
 
     /**
      * Multiply by a scalar to get a DpSquared result.
      */
+    @Stable
     inline operator fun times(other: Dp): DpCubed =
         DpCubed(value = value * other.value)
 
     /**
      * Support comparing DpSquared with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: DpSquared) =
         value.compareTo(other.value)
 
+    @Stable
     override fun toString(): String = "$value.dp^2"
 }
 
@@ -307,50 +350,59 @@
     /**
      * Add two DpCubed together.
      */
+    @Stable
     inline operator fun plus(dimension: DpCubed) =
         DpCubed(value = value + dimension.value)
 
     /**
      * Subtract a DpCubed from another one.
      */
+    @Stable
     inline operator fun minus(dimension: DpCubed) =
         DpCubed(value = value - dimension.value)
 
     /**
      * Divide a DpCubed by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): DpCubed =
         DpCubed(value = value / other)
 
     /**
      * Divide by a [Dp] to get a [DpSquared] result.
      */
+    @Stable
     inline operator fun div(other: Dp): DpSquared =
         DpSquared(value = value / other.value)
 
     /**
      * Divide by a [DpSquared] to get a [Dp] result.
      */
+    @Stable
     inline operator fun div(other: DpSquared): Dp =
         Dp(value = value / other.value)
 
     /**
      * Divide by a DpCubed to get a scalar result.
      */
+    @Stable
     inline operator fun div(other: DpCubed): Float = value / other.value
 
     /**
      * Multiply by a scalar to get a DpCubed result.
      */
+    @Stable
     inline operator fun times(other: Float): DpCubed =
         DpCubed(value = value * other)
 
     /**
      * Support comparing DpCubed with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: DpCubed) =
         value.compareTo(other.value)
 
+    @Stable
     override fun toString(): String = "$value.dp^3"
 }
 /**
@@ -368,50 +420,59 @@
     /**
      * Add two DpInverse together.
      */
+    @Stable
     inline operator fun plus(dimension: DpInverse) =
         DpInverse(value = value + dimension.value)
 
     /**
      * Subtract a DpInverse from another one.
      */
+    @Stable
     inline operator fun minus(dimension: DpInverse) =
         DpInverse(value = value - dimension.value)
 
     /**
      * Divide a DpInverse by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): DpInverse =
         DpInverse(value = value / other)
 
     /**
      * Multiply by a scalar to get a DpInverse result.
      */
+    @Stable
     inline operator fun times(other: Float): DpInverse =
         DpInverse(value = value * other)
 
     /**
      * Multiply by a [Dp] to get a scalar result.
      */
+    @Stable
     inline operator fun times(other: Dp): Float = value * other.value
 
     /**
      * Multiply by a [DpSquared] to get a [Dp] result.
      */
+    @Stable
     inline operator fun times(other: DpSquared): Dp =
         Dp(value = value * other.value)
 
     /**
      * Multiply by a [DpCubed] to get a [DpSquared] result.
      */
+    @Stable
     inline operator fun times(other: DpCubed): DpSquared =
         DpSquared(value = value * other.value)
 
     /**
      * Support comparing DpInverse with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: DpInverse) =
         value.compareTo(other.value)
 
+    @Stable
     override fun toString(): String = "$value.dp^-1"
 }
 
@@ -429,27 +490,32 @@
     /**
      * The horizontal aspect of the position in [Dp]
      */
+    @Stable
     /*inline*/ val x: Dp
         get() = unpackFloat1(value).dp
 
     /**
      * The vertical aspect of the position in [Dp]
      */
+    @Stable
     /*inline*/ val y: Dp
         get() = unpackFloat2(value).dp
 
     /**
      * Subtract a [Position] from another one.
      */
+    @Stable
     inline operator fun minus(other: Position) =
         Position(x - other.x, y - other.y)
 
     /**
      * Add a [Position] to another one.
      */
+    @Stable
     inline operator fun plus(other: Position) =
         Position(x + other.x, y + other.y)
 
+    @Stable
     override fun toString(): String = "($x, $y)"
 }
 
@@ -457,11 +523,13 @@
  * Constructs a [Position] from [x] and [y] position [Dp] values.
  */
 @OptIn(ExperimentalUnsignedTypes::class)
+@Stable
 inline fun Position(x: Dp, y: Dp): Position = Position(packFloats(x.value, y.value))
 
 /**
  * The magnitude of the offset represented by this [Position].
  */
+@Stable
 fun Position.getDistance(): Dp {
     return Dp(sqrt(x.value * x.value + y.value * y.value))
 }
@@ -477,6 +545,7 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
+@Stable
 fun lerp(start: Position, stop: Position, fraction: Float): Position =
     Position(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction))
 
@@ -485,18 +554,24 @@
  */
 @Immutable
 data class Bounds(
+    @Stable
     val left: Dp,
+    @Stable
     val top: Dp,
+    @Stable
     val right: Dp,
+    @Stable
     val bottom: Dp
 )
 
 /**
  * A width of this Bounds in [Dp].
  */
+@Stable
 inline val Bounds.width: Dp get() = right - left
 
 /**
  * A height of this Bounds in [Dp].
  */
+@Stable
 inline val Bounds.height: Dp get() = bottom - top
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt
index 5db5baa..ee02176 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt
@@ -19,6 +19,9 @@
 
 package androidx.ui.unit
 
+import androidx.compose.Immutable
+import androidx.compose.Stable
+
 /*
  * The following unit conversion factors are required to be public due to their use
  * from inline functions.
@@ -160,38 +163,45 @@
  *     val aLongWeekend = 86.hours
  *     assertEquals(3, aLongWeekend.inDays())
  */
+@Immutable
 data class Duration(val nanoseconds: Long) : Comparable<Duration> {
 
     /**
      * Adds this Duration and [other] and returns the sum as a Duration.
      */
+    @Stable
     operator fun plus(other: Duration) = Duration(nanoseconds + other.nanoseconds)
 
     /**
      * Subtracts [other] from this Duration and returns the difference.
      */
+    @Stable
     operator fun minus(other: Duration) = Duration(nanoseconds - other.nanoseconds)
 
     /**
      * Multiplies this Duration by the given [factor] and returns the result.
      */
+    @Stable
     operator fun times(factor: Int) = Duration(nanoseconds * factor)
 
     /**
      * Multiplies this Duration by the given [factor] and returns the result.
      */
+    @Stable
     operator fun times(factor: Double) = Duration((nanoseconds * factor).toLong())
 
     /**
      * Divides this Duration by the given [quotient] and returns the truncated
      * result as a Duration.
      */
+    @Stable
     operator fun div(quotient: Int) = Duration(nanoseconds / quotient)
 
     /**
      * Divides this Duration by the given [quotient] and returns the truncated
      * result as a Duration.
      */
+    @Stable
     operator fun div(quotient: Double) = Duration((nanoseconds / quotient).toLong())
 
     /**
@@ -205,6 +215,7 @@
      * It is always the case that `duration1.compareTo(duration2) < 0` if
      * `(someDate + duration1).compareTo(someDate + duration2) < 0`.
      */
+    @Stable
     override fun compareTo(other: Duration): Int = when {
         nanoseconds < other.nanoseconds -> -1
         nanoseconds == other.nanoseconds -> 0
@@ -220,6 +231,7 @@
      *     val d = Duration(days = 1, hours = 1, minutes = 33, microseconds = 500)
      *     d.toString()  // "25:33:00.000500"
      */
+    @Stable
     override fun toString(): String {
         if (inMicroseconds() < 0) {
             return "-${Duration(-nanoseconds)}"
@@ -232,6 +244,7 @@
 
     companion object {
         /** An empty Duration. No delay. Instant. */
+        @Stable
         val Zero = Duration(0)
     }
 }
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt
index 8e773cb0..1f90884 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt
@@ -18,6 +18,7 @@
 package androidx.ui.unit
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.unit.IntPx.Companion.Infinity
 import androidx.ui.util.lerp
 import androidx.ui.util.packInts
@@ -38,6 +39,7 @@
      * Add two [IntPx]s together. Any operation on an
      * [IntPx.Infinity] results in [IntPx.Infinity]
      */
+    @Stable
     operator fun plus(other: IntPx) =
         keepInfinity(other, IntPx(value = this.value + other.value))
 
@@ -45,6 +47,7 @@
      * Subtract a IntPx from another one. Any operation on an
      * [IntPx.Infinity] results in [IntPx.Infinity]
      */
+    @Stable
     operator fun minus(other: IntPx) =
         keepInfinity(other, IntPx(value = this.value - other.value))
 
@@ -52,12 +55,14 @@
      * This is the same as multiplying the IntPx by -1. Any operation on an
      * [IntPx.Infinity] results in [IntPx.Infinity]
      */
+    @Stable
     operator fun unaryMinus() = keepInfinity(IntPx(-value))
 
     /**
      * Divide a IntPx by a scalar and return the rounded result as an IntPx. Any operation on an
      * [IntPx.Infinity] results in [IntPx.Infinity]
      */
+    @Stable
     operator fun div(other: Float): IntPx =
         keepInfinity(IntPx(value = (value.toFloat() / other).roundToInt()))
 
@@ -65,6 +70,7 @@
      * Divide a IntPx by a scalar and return the rounded result as an IntPx. Any operation on an
      * [IntPx.Infinity] results in [IntPx.Infinity]
      */
+    @Stable
     operator fun div(other: Double): IntPx =
         keepInfinity(IntPx(value = (value.toDouble() / other).roundToInt()))
 
@@ -72,6 +78,7 @@
      * Divide a IntPx by a scalar and return the rounded result as an IntPx. Any operation on an
      * [IntPx.Infinity] results in [IntPx.Infinity]
      */
+    @Stable
     operator fun div(other: Int): IntPx =
         keepInfinity(IntPx(value = (value.toFloat() / other).roundToInt()))
 
@@ -79,37 +86,44 @@
      * Multiply a IntPx by a scalar and round the result to an IntPx. Any operation on an
      * [IntPx.Infinity] results in [IntPx.Infinity]
      */
+    @Stable
     operator fun times(other: Float): IntPx =
         keepInfinity(IntPx(value = (value.toFloat() * other).roundToInt()))
 
     /**
      * Multiply a IntPx by a scalar and round the result to an IntPx
      */
+    @Stable
     operator fun times(other: Double): IntPx =
         keepInfinity(IntPx(value = (value.toDouble() * other).roundToInt()))
 
     /**
      * Multiply a IntPx by a scalar and result in an IntPx
      */
+    @Stable
     operator fun times(other: Int): IntPx =
         keepInfinity(IntPx(value = value * other))
 
     /**
      * Returns the remainder of the IntPx when dividing by an integer.
      */
+    @Stable
     inline operator fun rem(other: Int): IntPx =
         IntPx(value = value % other)
 
     /**
      * Support comparing Dimensions with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: IntPx) = value.compareTo(other.value)
 
     /**
      * Compares this [IntPx] to another [Px]
      */
+    @Stable
     inline operator fun compareTo(other: Px) = value.compareTo(other.value)
 
+    @Stable
     override fun toString() = "$value.ipx"
 
     companion object {
@@ -119,11 +133,13 @@
          * that the particular dimension is not regulated and measurement should choose
          * the best option without any constraint.
          */
+        @Stable
         val Infinity = IntPx(value = Int.MAX_VALUE)
 
         /**
          * Zero IntPx dimension. Same as `0.ipx`.
          */
+        @Stable
         val Zero = IntPx(value = 0)
     }
 }
@@ -131,14 +147,17 @@
 /**
  * Return whether `true` when it is finite or `false` when it is [IntPx.Infinity]
  */
+@Stable
 inline fun IntPx.isFinite(): Boolean = value != Int.MAX_VALUE
 
 @PublishedApi
+@Stable
 internal inline fun IntPx.keepInfinity(other: IntPx, noInfinityValue: IntPx): IntPx {
     return if (!isFinite() || !other.isFinite()) Infinity else noInfinityValue
 }
 
 @PublishedApi
+@Stable
 internal inline fun IntPx.keepInfinity(noInfinityValue: IntPx): IntPx {
     return if (!isFinite()) this else noInfinityValue
 }
@@ -150,12 +169,14 @@
  *     // -- or --
  *     val y = 10.ipx
  */
+@Stable
 inline val Int.ipx: IntPx get() = IntPx(value = this)
 
 /**
  * Multiply an IntPx by a Float and round the result to an IntPx. Any operation on an
  * [IntPx.Infinity] results in [IntPx.Infinity]
  */
+@Stable
 inline operator fun Float.times(other: IntPx): IntPx =
     other.keepInfinity(IntPx(value = (other.value.toFloat() * this).roundToInt()))
 
@@ -163,6 +184,7 @@
  * Multiply an IntPx by a Double and round the result to an IntPx. Any operation on an
  * [IntPx.Infinity] results in [IntPx.Infinity]
  */
+@Stable
 inline operator fun Double.times(other: IntPx): IntPx =
     other.keepInfinity(IntPx(value = (other.value.toDouble() * this).roundToInt()))
 
@@ -170,18 +192,21 @@
  * Multiply an IntPx by a Double to result in an IntPx. Any operation on an
  * [IntPx.Infinity] results in [IntPx.Infinity]
  */
+@Stable
 inline operator fun Int.times(other: IntPx): IntPx =
     other.keepInfinity(IntPx(value = other.value * this))
 
 /**
  * Return the minimum of two [IntPx]s. Any value is considered less than [IntPx.Infinity].
  */
+@Stable
 inline fun min(a: IntPx, b: IntPx): IntPx =
     IntPx(value = min(a.value, b.value))
 
 /**
  * Return the maximum of two [IntPx]s. An [IntPx.Infinity] is considered the maximum value.
  */
+@Stable
 inline fun max(a: IntPx, b: IntPx): IntPx =
     IntPx(value = max(a.value, b.value))
 
@@ -191,6 +216,7 @@
  * @return this value if it's in the range, or [minimumValue] if this value is less than
  * [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
  */
+@Stable
 inline fun IntPx.coerceIn(minimumValue: IntPx, maximumValue: IntPx): IntPx =
     IntPx(value = value.coerceIn(minimumValue.value, maximumValue.value))
 
@@ -200,6 +226,7 @@
  * @return this value if it's greater than or equal to the [minimumValue] or the
  * [minimumValue] otherwise.
  */
+@Stable
 inline fun IntPx.coerceAtLeast(minimumValue: IntPx): IntPx =
     IntPx(value = value.coerceAtLeast(minimumValue.value))
 
@@ -210,6 +237,7 @@
  * [maximumValue] otherwise. Passing [IntPx.Infinity] as [maximumValue] will
  * always return this.
  */
+@Stable
 inline fun IntPx.coerceAtMost(maximumValue: IntPx): IntPx =
     IntPx(value = value.coerceAtMost(maximumValue.value))
 
@@ -226,6 +254,7 @@
  *
  * If [start] or [stop] is [IntPx.Infinity], then [IntPx.Infinity] is returned.
  */
+@Stable
 fun lerp(start: IntPx, stop: IntPx, fraction: Float): IntPx {
     return start.keepInfinity(stop, IntPx(lerp(start.value, stop.value, fraction)))
 }
@@ -233,12 +262,14 @@
 /**
  * Rounds a [Px] size to the nearest Int pixel value.
  */
+@Stable
 inline fun Px.round(): IntPx =
     if (value.isInfinite()) Infinity else IntPx(value.roundToInt())
 
 /**
  * Rounds up a [Px] to the smallest integer value that is not less than the original value.
  */
+@Stable
 inline fun Px.ceil(): IntPx =
     if (value.isInfinite()) Infinity else IntPx(kotlin.math.ceil(value).toInt())
 
@@ -257,33 +288,39 @@
     /**
      * The horizontal aspect of the size in [IntPx].
      */
+    @Stable
     inline val width: IntPx
         get() = unpackInt1(value).ipx
 
     /**
      * The vertical aspect of the size in [IntPx].
      */
+    @Stable
     inline val height: IntPx
         get() = unpackInt2(value).ipx
 
     /**
      * Returns an IntPxSize scaled by multiplying [width] and [height] by [other]
      */
+    @Stable
     inline operator fun times(other: Int): IntPxSize =
         IntPxSize(width = width * other, height = height * other)
 
     /**
      * Returns an IntPxSize scaled by dividing [width] and [height] by [other]
      */
+    @Stable
     inline operator fun div(other: Int): IntPxSize =
         IntPxSize(width = width / other, height = height / other)
 
+    @Stable
     override fun toString(): String = "$width x $height"
 
     companion object {
         /**
          * [IntPxSize] with zero values.
          */
+        @Stable
         val Zero = IntPxSize(0.ipx, 0.ipx)
     }
 }
@@ -292,6 +329,7 @@
  * Constructs an [IntPxSize] from width and height [IntPx] values.
  */
 @OptIn(ExperimentalUnsignedTypes::class)
+@Stable
 inline fun IntPxSize(width: IntPx, height: IntPx): IntPxSize =
     IntPxSize(packInts(width.value, height.value))
 
@@ -299,12 +337,14 @@
  * Returns an [IntPxSize] with [size]'s [IntPxSize.width] and [IntPxSize.height]
  * multiplied by [this]
  */
+@Stable
 inline operator fun Int.times(size: IntPxSize) = size * this
 
 /**
  * Returns the [IntPxPosition] of the center of the rect from the point of [0, 0]
  * with this [IntPxSize].
  */
+@Stable
 fun IntPxSize.center(): IntPxPosition {
     return IntPxPosition(width / 2f, height / 2f)
 }
@@ -320,35 +360,41 @@
     /**
      * The horizontal aspect of the position in [IntPx]
      */
+    @Stable
     inline val x: IntPx
         get() = unpackInt1(value).ipx
 
     /**
      * The vertical aspect of the position in [IntPx]
      */
+    @Stable
     inline val y: IntPx
         get() = unpackInt2(value).ipx
 
     /**
      * Subtract a [IntPxPosition] from another one.
      */
+    @Stable
     inline operator fun minus(other: IntPxPosition) =
         IntPxPosition(x - other.x, y - other.y)
 
     /**
      * Add a [IntPxPosition] to another one.
      */
+    @Stable
     inline operator fun plus(other: IntPxPosition) =
         IntPxPosition(x + other.x, y + other.y)
 
     /**
      * Returns a new PxPosition representing the negation of this point.
      */
+    @Stable
     inline operator fun unaryMinus() = IntPxPosition(-x, -y)
 
     override fun toString(): String = "($x, $y)"
 
     companion object {
+        @Stable
         val Origin = IntPxPosition(0.ipx, 0.ipx)
     }
 }
@@ -371,12 +417,14 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
+@Stable
 fun lerp(start: IntPxPosition, stop: IntPxPosition, fraction: Float): IntPxPosition =
     IntPxPosition(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction))
 
 /**
  * A four dimensional bounds using [IntPx] for units
  */
+@Immutable
 data class IntPxBounds(
     val left: IntPx,
     val top: IntPx,
@@ -387,16 +435,19 @@
 /**
  * A width of this IntPxBounds in [IntPx].
  */
+@Stable
 inline val IntPxBounds.width: IntPx get() = right - left
 
 /**
  * A height of this IntPxBounds in [IntPx].
  */
+@Stable
 inline val IntPxBounds.height: IntPx get() = bottom - top
 
 /**
  * Returns the [IntPxPosition] of the center of the [IntPxBounds].
  */
+@Stable
 inline fun IntPxBounds.center(): IntPxPosition {
     return IntPxPosition(left + width / 2f, top + height / 2f)
 }
@@ -404,6 +455,7 @@
 /**
  * Convert a [IntPxBounds] to a [IntPxSize].
  */
+@Stable
 inline fun IntPxBounds.toSize(): IntPxSize {
     return IntPxSize(width, height)
 }
@@ -411,20 +463,24 @@
 /**
  * Create a [PxSize] from [IntPx] values.
  */
+@Stable
 inline fun PxSize(width: IntPx, height: IntPx): PxSize =
     PxSize(width = width.toPx(), height = height.toPx())
 
 /**
  * Create a [PxPosition] from [IntPx] values.
  */
+@Stable
 inline fun PxPosition(x: IntPx, y: IntPx): PxPosition = PxPosition(x = x.toPx(), y = y.toPx())
 
 /**
  * Convert a [IntPxPosition] to a [PxPosition]
  */
+@Stable
 inline fun IntPxPosition.toPxPosition(): PxPosition = PxPosition(this.x, this.y)
 
 /**
  * Convert a [IntPxSize] to a [PxSize]
  */
+@Stable
 inline fun IntPxSize.toPxSize(): PxSize = PxSize(this.width, this.height)
\ No newline at end of file
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt
index 81386d2..e30c25e 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt
@@ -19,6 +19,7 @@
 package androidx.ui.unit
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.packInts
 import androidx.ui.util.unpackInt1
 import androidx.ui.util.unpackInt2
@@ -32,28 +33,32 @@
     /**
      * The horizontal aspect of the size in [Int] pixels.
      */
+    @Stable
     val width: Int
         get() = unpackInt1(value)
 
     /**
      * The vertical aspect of the size in [Int] pixels.
      */
+    @Stable
     val height: Int
         get() = unpackInt2(value)
 
     /**
      * Returns an IntSize scaled by multiplying [width] and [height] by [other]
      */
-
+    @Stable
     operator fun times(other: Int): IntSize =
         IntSize(width = width * other, height = height * other)
 
     /**
      * Returns an IntSize scaled by dividing [width] and [height] by [other]
      */
+    @Stable
     operator fun div(other: Int): IntSize =
         IntSize(width = width / other, height = height / other)
 
+    @Stable
     override fun toString(): String = "$width x $height"
 }
 
@@ -61,10 +66,12 @@
  * Returns an [IntSize] with [size]'s [IntSize.width] and [IntSize.height]
  * multiplied by [this].
  */
+@Stable
 operator fun Int.times(size: IntSize) = size * this
 
 /**
  * Constructs an [IntPxSize] from width and height [IntPx] values.
  */
+@Stable
 fun IntSize(width: Int, height: Int): IntSize =
     IntSize(packInts(width, height))
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
index d4af92c..753049d 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
@@ -18,6 +18,7 @@
 package androidx.ui.unit
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.geometry.Offset
 import androidx.ui.geometry.Rect
 import androidx.ui.util.lerp
@@ -46,94 +47,112 @@
     /**
      * Add two [Px]s together.
      */
+    @Stable
     inline operator fun plus(other: Px) =
         Px(value = this.value + other.value)
 
     /**
      * Subtract a Px from another one.
      */
+    @Stable
     inline operator fun minus(other: Px) =
         Px(value = this.value - other.value)
 
     /**
      * This is the same as multiplying the Px by -1.0.
      */
+    @Stable
     inline operator fun unaryMinus() = Px(-value)
 
     /**
      * Divide a Px by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): Px =
         Px(value = value / other)
 
+    @Stable
     inline operator fun div(other: Int): Px =
         Px(value = value / other)
 
     /**
      * Divide by another Px to get a scalar.
      */
+    @Stable
     inline operator fun div(other: Px): Float = value / other.value
 
     /**
      * Divide by [PxSquared] to get a [PxInverse].
      */
+    @Stable
     inline operator fun div(other: PxSquared): PxInverse =
         PxInverse(value = value / other.value)
 
     /**
      * Multiply a Px by a scalar.
      */
+    @Stable
     inline operator fun times(other: Float): Px =
         Px(value = value * other)
 
+    @Stable
     inline operator fun times(other: Int): Px =
         Px(value = value * other)
 
     /**
      * Multiply by a Px to get a [PxSquared] result.
      */
+    @Stable
     inline operator fun times(other: Px): PxSquared =
         PxSquared(value = value * other.value)
 
     /**
      * Multiply by a Px to get a [PxSquared] result.
      */
+    @Stable
     inline operator fun times(other: PxSquared): PxCubed =
         PxCubed(value = value * other.value)
 
     /**
      * Compare [Px] with another [Px].
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: Px) = value.compareTo(other.value)
 
     /**
      * Compares this [Px] to another [IntPx]
      */
+    @Stable
     inline operator fun compareTo(other: IntPx): Int = value.compareTo(other.value)
 
     /**
      * Add an [IntPx] to this [Px].
      */
+    @Stable
     inline operator fun plus(other: IntPx) =
         Px(value = this.value + other.value)
 
     /**
      * Subtract an [IntPx] from this [Px].
      */
+    @Stable
     inline operator fun minus(other: IntPx) =
         Px(value = this.value - other.value)
 
+    @Stable
     override fun toString() = "$value.px"
 
     companion object {
         /**
          * Infinite px dimension.
          */
+        @Stable
         val Infinity = Px(value = Float.POSITIVE_INFINITY)
 
         /**
          * Zero px dimension
          */
+        @Stable
         val Zero = Px(0.0f)
     }
 }
@@ -145,6 +164,7 @@
  *     // -- or --
  *     val y = 10.px
  */
+@Stable
 inline val Int.px: Px get() = Px(value = this.toFloat())
 
 /**
@@ -154,6 +174,7 @@
  *     // -- or --
  *     val y = 10.0.px
  */
+@Stable
 inline val Double.px: Px get() = Px(value = this.toFloat())
 
 /**
@@ -163,30 +184,40 @@
  *     // -- or --
  *     val y = 10f.px
  */
+@Stable
 inline val Float.px: Px get() = Px(value = this)
 
+@Stable
 inline operator fun Float.div(other: Px) =
     PxInverse(this / other.value)
 
+@Stable
 inline operator fun Double.div(other: Px) =
     PxInverse(this.toFloat() / other.value)
 
+@Stable
 inline operator fun Int.div(other: Px) =
     PxInverse(this / other.value)
 
+@Stable
 inline operator fun Float.times(other: Px) =
     Px(this * other.value)
 
+@Stable
 inline operator fun Double.times(other: Px) =
     Px(this.toFloat() * other.value)
 
+@Stable
 inline operator fun Int.times(other: Px) =
     Px(this * other.value)
 
+@Stable
 inline fun min(a: Px, b: Px): Px = Px(value = min(a.value, b.value))
 
+@Stable
 inline fun max(a: Px, b: Px): Px = Px(value = max(a.value, b.value))
 
+@Stable
 inline fun abs(x: Px): Px = Px(abs(x.value))
 
 /**
@@ -195,6 +226,7 @@
  * @return this value if it's in the range, or [minimumValue] if this value is less than
  * [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
  */
+@Stable
 inline fun Px.coerceIn(minimumValue: Px, maximumValue: Px): Px =
     Px(value = value.coerceIn(minimumValue.value, maximumValue.value))
 
@@ -204,6 +236,7 @@
  * @return this value if it's greater than or equal to the [minimumValue] or the
  * [minimumValue] otherwise.
  */
+@Stable
 inline fun Px.coerceAtLeast(minimumValue: Px): Px =
     Px(value = value.coerceAtLeast(minimumValue.value))
 
@@ -213,6 +246,7 @@
  * @return this value if it's less than or equal to the [maximumValue] or the
  * [maximumValue] otherwise.
  */
+@Stable
 inline fun Px.coerceAtMost(maximumValue: Px): Px =
     Px(value = value.coerceAtMost(maximumValue.value))
 
@@ -227,6 +261,7 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
+@Stable
 fun lerp(start: Px, stop: Px, fraction: Float): Px {
     return Px(lerp(start.value, stop.value, fraction))
 }
@@ -246,56 +281,66 @@
     /**
      * Add two DimensionSquares together.
      */
+    @Stable
     inline operator fun plus(other: PxSquared) =
         PxSquared(value = value + other.value)
 
     /**
      * Subtract a DimensionSquare from another one.
      */
+    @Stable
     inline operator fun minus(other: PxSquared) =
         PxSquared(value = value - other.value)
 
     /**
      * Divide a DimensionSquare by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): PxSquared =
         PxSquared(value = value / other)
 
     /**
      * Divide by a [Px] to get a [Px] result.
      */
+    @Stable
     inline operator fun div(other: Px): Px =
         Px(value = value / other.value)
 
     /**
      * Divide by a PxSquared to get a scalar result.
      */
+    @Stable
     inline operator fun div(other: PxSquared): Float = value / other.value
 
     /**
      * Divide by a [PxCubed] to get a [PxInverse] result.
      */
+    @Stable
     inline operator fun div(other: PxCubed): PxInverse =
         PxInverse(value / other.value)
 
     /**
      * Multiply by a scalar to get a PxSquared result.
      */
+    @Stable
     inline operator fun times(other: Float): PxSquared =
         PxSquared(value = value * other)
 
     /**
      * Multiply by a scalar to get a PxSquared result.
      */
+    @Stable
     inline operator fun times(other: Px): PxCubed =
         PxCubed(value = value * other.value)
 
     /**
      * Support comparing PxSquared with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: PxSquared) =
         value.compareTo(other.value)
 
+    @Stable
     override fun toString(): String = "$value.px^2"
 }
 
@@ -314,50 +359,59 @@
     /**
      * Add two PxCubed together.
      */
+    @Stable
     inline operator fun plus(dimension: PxCubed) =
         PxCubed(value = value + dimension.value)
 
     /**
      * Subtract a PxCubed from another one.
      */
+    @Stable
     inline operator fun minus(dimension: PxCubed) =
         PxCubed(value = value - dimension.value)
 
     /**
      * Divide a PxCubed by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): PxCubed =
         PxCubed(value = value / other)
 
     /**
      * Divide by a [Px] to get a [PxSquared] result.
      */
+    @Stable
     inline operator fun div(other: Px): PxSquared =
         PxSquared(value = value / other.value)
 
     /**
      * Divide by a [PxSquared] to get a [Px] result.
      */
+    @Stable
     inline operator fun div(other: PxSquared): Px =
         Px(value = value / other.value)
 
     /**
      * Divide by a PxCubed to get a scalar result.
      */
+    @Stable
     inline operator fun div(other: PxCubed): Float = value / other.value
 
     /**
      * Multiply by a scalar to get a PxCubed result.
      */
+    @Stable
     inline operator fun times(other: Float): PxCubed =
         PxCubed(value = value * other)
 
     /**
      * Support comparing PxCubed with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: PxCubed) =
         value.compareTo(other.value)
 
+    @Stable
     override fun toString(): String = "$value.px^3"
 }
 
@@ -376,50 +430,59 @@
     /**
      * Add two PxInverse together.
      */
+    @Stable
     inline operator fun plus(dimension: PxInverse) =
         PxInverse(value = value + dimension.value)
 
     /**
      * Subtract a PxInverse from another one.
      */
+    @Stable
     inline operator fun minus(dimension: PxInverse) =
         PxInverse(value = value - dimension.value)
 
     /**
      * Divide a PxInverse by a scalar.
      */
+    @Stable
     inline operator fun div(other: Float): PxInverse =
         PxInverse(value = value / other)
 
     /**
      * Multiply by a scalar to get a PxInverse result.
      */
+    @Stable
     inline operator fun times(other: Float): PxInverse =
         PxInverse(value = value * other)
 
     /**
      * Multiply by a [Px] to get a scalar result.
      */
+    @Stable
     inline operator fun times(other: Px): Float = value * other.value
 
     /**
      * Multiply by a [PxSquared] to get a [Px] result.
      */
+    @Stable
     inline operator fun times(other: PxSquared): Px =
         Px(value = value * other.value)
 
     /**
      * Multiply by a [PxCubed] to get a [PxSquared] result.
      */
+    @Stable
     inline operator fun times(other: PxCubed): PxSquared =
         PxSquared(value = value * other.value)
 
     /**
      * Support comparing PxInverse with comparison operators.
      */
+    @Stable
     override /* TODO: inline */ operator fun compareTo(other: PxInverse) =
         value.compareTo(other.value)
 
+    @Stable
     override fun toString(): String = "$value.px^-1"
 }
 
@@ -436,60 +499,71 @@
     /**
      * The horizontal aspect of the size in [Px].
      */
+    @Stable
     inline val width: Px
         get() = unpackFloat1(value).px
 
     /**
      * The vertical aspect of the size in [Px].
      */
+    @Stable
     inline val height: Px
         get() = unpackFloat2(value).px
 
     /**
      * Returns a PxSize scaled by multiplying [width] and [height] by [other]
      */
+    @Stable
     inline operator fun times(other: Int): PxSize =
         PxSize(width = width * other, height = height * other)
 
     /**
      * Returns a PxSize scaled  by multiplying [width] and [height] by [other]
      */
+    @Stable
     inline operator fun times(other: Float): PxSize =
         PxSize(width = width * other, height = height * other)
 
     /**
      * Returns a PxSize scaled  by multiplying [width] and [height] by [other]
      */
+    @Stable
     inline operator fun times(other: Double): PxSize = times(other.toFloat())
 
     /**
      * Returns a PxSize scaled  by dividing [width] and [height] by [other]
      */
+    @Stable
     inline operator fun div(other: Int): PxSize =
         PxSize(width = width / other, height = height / other)
 
     /**
      * Returns a PxSize scaled  by dividing [width] and [height] by [other]
      */
+    @Stable
     inline operator fun div(other: Float): PxSize =
         PxSize(width = width / other, height = height / other)
 
     /**
      * Returns a PxSize scaled  by dividing [width] and [height] by [other]
      */
+    @Stable
     inline operator fun div(other: Double): PxSize = div(other.toFloat())
 
+    @Stable
     override fun toString(): String = "$width x $height"
 
     companion object {
         /**
          * [PxSize] with zero values.
          */
+        @Stable
         val Zero = PxSize(0.px, 0.px)
 
         /**
          * Default value indicating no specified size
          */
+        @Stable
         val UnspecifiedSize = PxSize(Px.Infinity, Px.Infinity)
     }
 }
@@ -497,32 +571,38 @@
 /**
  * Constructs a [PxSize] from width and height Float values.
  */
+@Stable
 inline fun PxSize(width: Float, height: Float): PxSize = PxSize(packFloats(width, height))
 
 /**
  * Constructs a [PxSize] from width and height [Px] values.
  */
+@Stable
 inline fun PxSize(width: Px, height: Px): PxSize = PxSize(packFloats(width.value, height.value))
 
 /**
  * Returns a [PxSize] with [size]'s [PxSize.width] and [PxSize.height] multiplied by [this]
  */
+@Stable
 inline operator fun Int.times(size: PxSize) = size * this
 
 /**
  * Returns a [PxSize] with [size]'s [PxSize.width] and [PxSize.height] multiplied by [this]
  */
+@Stable
 inline operator fun Float.times(size: PxSize) = size * this
 
 /**
  * Returns a [PxSize] with [size]'s [PxSize.width] and [PxSize.height] multiplied by [this]
  */
+@Stable
 inline operator fun Double.times(size: PxSize) = size * this
 
 /**
  * Returns the [PxPosition] of the center of the rect from the point of [0, 0]
  * with this [PxSize].
  */
+@Stable
 fun PxSize.center(): PxPosition {
     return PxPosition(width / 2f, height / 2f)
 }
@@ -530,6 +610,7 @@
 /**
  * Returns the smallest dimension size.
  */
+@Stable
 val PxSize.minDimension get() = min(width, height)
 
 /**
@@ -540,47 +621,56 @@
     /**
      * The horizontal aspect of the position in [Px]
      */
+    @Stable
     inline val x: Px
         get() = unpackFloat1(value).px
 
     /**
      * The vertical aspect of the position in [Px]
      */
+    @Stable
     inline val y: Px
         get() = unpackFloat2(value).px
 
     /**
      * Subtract a [PxPosition] from another one.
      */
+    @Stable
     inline operator fun minus(other: PxPosition) =
         PxPosition(x - other.x, y - other.y)
 
     /**
      * Add a [PxPosition] to another one.
      */
+    @Stable
     inline operator fun plus(other: PxPosition) =
         PxPosition(x + other.x, y + other.y)
 
     /**
      * Subtract a [IntPxPosition] from this [PxPosition].
      */
+    @Stable
     inline operator fun minus(other: IntPxPosition) =
         PxPosition(x - other.x, y - other.y)
 
     /**
      * Add a [IntPxPosition] to this [PxPosition].
      */
+    @Stable
     inline operator fun plus(other: IntPxPosition) =
         PxPosition(x + other.x, y + other.y)
 
     /**
      * Returns a new PxPosition representing the negation of this point.
      */
+    @Stable
     inline operator fun unaryMinus() = PxPosition(-x, -y)
 
+    @Stable
     override fun toString(): String = "($x, $y)"
 
     companion object {
+        @Stable
         val Origin = PxPosition(0.px, 0.px)
     }
 }
@@ -588,26 +678,31 @@
 /**
  * Constructs a [PxPosition] from [x] and [y] position float values.
  */
+@Stable
 inline fun PxPosition(x: Float, y: Float): PxPosition = PxPosition(packFloats(x, y))
 
 /**
  * Constructs a [PxPosition] from [x] and [y] position [Px] values.
  */
+@Stable
 inline fun PxPosition(x: Px, y: Px): PxPosition = PxPosition(packFloats(x.value, y.value))
 
 /**
  * The magnitude of the offset represented by this [PxPosition].
  */
+@Stable
 fun PxPosition.getDistance(): Px = Px(sqrt(x.value * x.value + y.value * y.value))
 
 /**
  * Convert a [PxPosition] to a [Offset].
  */
+@Stable
 inline fun PxPosition.toOffset(): Offset = Offset(x.value, y.value)
 
 /**
  * Round a [PxPosition] down to the nearest [Int] coordinates.
  */
+@Stable
 inline fun PxPosition.round(): IntPxPosition = IntPxPosition(x.round(), y.round())
 
 /**
@@ -621,6 +716,7 @@
  * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
  * 1.0, so negative values and values greater than 1.0 are valid.
  */
+@Stable
 fun lerp(start: PxPosition, stop: PxPosition, fraction: Float): PxPosition =
     PxPosition(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction))
 
@@ -635,6 +731,7 @@
     val bottom: Px
 )
 
+@Stable
 inline fun PxBounds(topLeft: PxPosition, size: PxSize) =
     PxBounds(
         left = topLeft.x,
@@ -646,16 +743,19 @@
 /**
  * A width of this PxBounds in [Px].
  */
+@Stable
 inline val PxBounds.width: Px get() = right - left
 
 /**
  * A height of this PxBounds in [Px].
  */
+@Stable
 inline val PxBounds.height: Px get() = bottom - top
 
 /**
  * Returns the [PxPosition] of the center of the [PxBounds].
  */
+@Stable
 inline fun PxBounds.center(): PxPosition {
     return PxPosition(left + width / 2f, top + height / 2f)
 }
@@ -663,6 +763,7 @@
 /**
  * Convert a [PxBounds] to a [PxSize].
  */
+@Stable
 fun PxBounds.toSize(): PxSize {
     return PxSize(width, height)
 }
@@ -671,6 +772,7 @@
  * Convert a [PxSize] to a [PxBounds]. The left and top are 0.px and the right and bottom
  * are the width and height, respectively.
  */
+@Stable
 fun PxSize.toBounds(): PxBounds {
     return PxBounds(0.px, 0.px, width, height)
 }
@@ -678,6 +780,7 @@
 /**
  * Convert a [PxBounds] to a [Rect].
  */
+@Stable
 fun PxBounds.toRect(): Rect {
     return Rect(
         left.value,
@@ -690,6 +793,7 @@
 /**
  * Convert a [PxSize] to a [Rect].
  */
+@Stable
 fun PxSize.toRect(): Rect {
     return Rect(0f, 0f, width.value, height.value)
 }
\ No newline at end of file
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
index d90d823..deda1b1 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
@@ -18,6 +18,7 @@
 package androidx.ui.unit
 
 import androidx.compose.Immutable
+import androidx.compose.Stable
 import androidx.ui.util.lerp
 
 /**
@@ -207,6 +208,7 @@
         /**
          * A special [TextUnit] instance for representing inheriting from parent value.
          */
+        @Stable
         val Inherit = pack(UNIT_TYPE_INHERIT, 0f)
     }
 
@@ -249,31 +251,37 @@
 /**
  * Creates a SP unit [TextUnit]
  */
+@Stable
 val Float.sp: TextUnit get() = pack(UNIT_TYPE_SP, this)
 
 /**
  * Creates an EM unit [TextUnit]
  */
+@Stable
 val Float.em: TextUnit get() = pack(UNIT_TYPE_EM, this)
 
 /**
  * Creates a SP unit [TextUnit]
  */
+@Stable
 val Double.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
 
 /**
  * Creates an EM unit [TextUnit]
  */
+@Stable
 val Double.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
 
 /**
  * Creates a SP unit [TextUnit]
  */
+@Stable
 val Int.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
 
 /**
  * Creates an EM unit [TextUnit]
  */
+@Stable
 val Int.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
 
 /**
@@ -282,6 +290,7 @@
  * This operation works only if the right operand is not equal to [TextUnit.Inherit].
  * The result of this operation is the same unit type of the given one.
  */
+@Stable
 inline operator fun Float.times(other: TextUnit) = checkArithmetic(other) {
     pack(other.rawType, this * other.value)
 }
@@ -292,6 +301,7 @@
  * This operation works only if the right operand is not equal to [TextUnit.Inherit].
  * The result of this operation is the same unit type of the given one.
  */
+@Stable
 inline operator fun Double.times(other: TextUnit) = checkArithmetic(other) {
     pack(other.rawType, this.toFloat() * other.value)
 }
@@ -302,6 +312,7 @@
  * This operation works only if the right operand is not equal to [TextUnit.Inherit].
  * The result of this operation is the same unit type of the given one.
  */
+@Stable
 inline operator fun Int.times(other: TextUnit) = checkArithmetic(other) {
     pack(other.rawType, this * other.value)
 }
@@ -313,6 +324,7 @@
  * equal to [TextUnit.Inherit].
  * The result of this operation is the same unit type of the given one.
  */
+@Stable
 inline fun min(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
     if (a.value < b.value) a else b
 }
@@ -324,6 +336,7 @@
  * equal to [TextUnit.Inherit].
  * The result of this operation is the same unit type of the given one.
  */
+@Stable
 inline fun max(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
     if (a.value < b.value) b else a
 }
@@ -339,6 +352,7 @@
  * @return this value if it's in the range, or [minimumValue] if this value is less than
  * [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
  */
+@Stable
 inline fun TextUnit.coerceIn(minimumValue: TextUnit, maximumValue: TextUnit): TextUnit =
     checkArithmetic(this, minimumValue, maximumValue) {
         pack(rawType, value.coerceIn(minimumValue.value, maximumValue.value))
@@ -350,6 +364,7 @@
  * @return this value if it's greater than or equal to the [minimumValue] or the
  * [minimumValue] otherwise.
  */
+@Stable
 inline fun TextUnit.coerceAtLeast(minimumValue: TextUnit): TextUnit =
     checkArithmetic(this, minimumValue) {
         pack(rawType, value.coerceAtLeast(minimumValue.value))
@@ -361,6 +376,7 @@
  * @return this value if it's less than or equal to the [maximumValue] or the
  * [maximumValue] otherwise.
  */
+@Stable
 inline fun TextUnit.coerceAtMost(maximumValue: TextUnit): TextUnit =
     checkArithmetic(this, maximumValue) {
         pack(rawType, value.coerceAtMost(maximumValue.value))
@@ -401,6 +417,7 @@
     return block()
 }
 
+@Stable
 fun lerp(a: TextUnit, b: TextUnit, t: Float): TextUnit = checkArithmetic(a, b) {
     return pack(a.rawType, lerp(a.value, b.value, t))
 }
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt
index 2348562..b870169 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt
@@ -16,7 +16,10 @@
 
 package androidx.ui.unit
 
+import androidx.compose.Immutable
+
 /** A velocity in two dimensions. */
+@Immutable
 data class Velocity(
     /** The number of pixels per second of velocity in the x and y directions. */
     val pixelsPerSecond: PxPosition