Add other terminal Rtis

Change-Id: If70dc283a5fb06fa0d037fb0d30e4ba19352f8d3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105736
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/sdk/lib/_internal/js_runtime/lib/rti.dart b/sdk/lib/_internal/js_runtime/lib/rti.dart
index b9eb479..a3480c2 100644
--- a/sdk/lib/_internal/js_runtime/lib/rti.dart
+++ b/sdk/lib/_internal/js_runtime/lib/rti.dart
@@ -110,7 +110,6 @@
   /// - Underlying type for unary terms.
   /// - Class part of a type environment inside a generic class, or `null` for
   ///   type tuple.
-  /// - Return type of function types.
   dynamic _primary;
 
   static Object _getPrimary(Rti rti) => rti._primary;
@@ -225,6 +224,9 @@
   int kind = Rti._getKind(rti);
 
   if (kind == Rti.kindDynamic) return 'dynamic';
+  if (kind == Rti.kindVoid) return 'void';
+  if (kind == Rti.kindNever) return 'Never';
+  if (kind == Rti.kindAny) return 'any';
 
   if (kind == Rti.kindInterface) {
     String name = Rti._getInterfaceName(rti);
@@ -256,6 +258,9 @@
   int kind = Rti._getKind(rti);
 
   if (kind == Rti.kindDynamic) return 'dynamic';
+  if (kind == Rti.kindVoid) return 'void';
+  if (kind == Rti.kindNever) return 'Never';
+  if (kind == Rti.kindAny) return 'any';
 
   if (kind == Rti.kindInterface) {
     String name = Rti._getInterfaceName(rti);
@@ -396,18 +401,39 @@
   // * `createXXX` to create the type if it does not exist.
 
   static String _canonicalRecipeOfDynamic() => '@';
+  static String _canonicalRecipeOfVoid() => '~';
+  static String _canonicalRecipeOfNever() => '0&';
+  static String _canonicalRecipeOfAny() => '1&';
 
   static Rti _lookupDynamicRti(universe) {
-    var cache = evalCache(universe);
-    var probe = _cacheGet(cache, _canonicalRecipeOfDynamic());
-    if (probe != null) return _castToRti(probe);
-    return _createDynamicRti(universe);
+    return _lookupTerminalRti(
+        universe, Rti.kindDynamic, _canonicalRecipeOfDynamic());
   }
 
-  static Rti _createDynamicRti(Object universe) {
+  static Rti _lookupVoidRti(universe) {
+    return _lookupTerminalRti(universe, Rti.kindVoid, _canonicalRecipeOfVoid());
+  }
+
+  static Rti _lookupNeverRti(universe) {
+    return _lookupTerminalRti(
+        universe, Rti.kindNever, _canonicalRecipeOfNever());
+  }
+
+  static Rti _lookupAnyRti(universe) {
+    return _lookupTerminalRti(universe, Rti.kindAny, _canonicalRecipeOfAny());
+  }
+
+  static Rti _lookupTerminalRti(universe, int kind, String canonicalRecipe) {
+    var cache = evalCache(universe);
+    var probe = _cacheGet(cache, canonicalRecipe);
+    if (probe != null) return _castToRti(probe);
+    return _createTerminalRti(universe, kind, canonicalRecipe);
+  }
+
+  static Rti _createTerminalRti(universe, int kind, String canonicalRecipe) {
     var rti = Rti.allocate();
-    Rti._setKind(rti, Rti.kindDynamic);
-    Rti._setCanonicalRecipe(rti, _canonicalRecipeOfDynamic());
+    Rti._setKind(rti, kind);
+    Rti._setCanonicalRecipe(rti, canonicalRecipe);
     return _finishRti(universe, rti);
   }
 
@@ -501,9 +527,9 @@
 ///
 ///   Period may be in any position, including first and last e.g. `.x`.
 ///
-/// ',': ignored
+/// ',':  ---
 ///
-///   Used to separate elements.
+///   Ignored. Used to separate elements.
 ///
 /// ';': item  ---  ToType(item)
 ///
@@ -511,8 +537,15 @@
 ///
 /// '@': --- dynamicType
 ///
+/// '~': --- voidType
+///
 /// '?':  type  ---  type?
 ///
+/// '&':  0  ---  NeverType
+/// '&':  1  ---  anyType
+///
+///   Escape op-code with small integer values for encoding rare operations.
+///
 /// '<':  --- position
 ///
 ///   Saves (pushes) position register, sets position register to end of stack.
@@ -650,13 +683,17 @@
             // ignored
             break;
 
+          case $SEMICOLON:
+            push(stack,
+                toType(universe(parser), environment(parser), pop(stack)));
+            break;
+
           case $AT:
             push(stack, _Universe._lookupDynamicRti(universe(parser)));
             break;
 
-          case $SEMICOLON:
-            push(stack,
-                toType(universe(parser), environment(parser), pop(stack)));
+          case $TILDE:
+            push(stack, _Universe._lookupVoidRti(universe(parser)));
             break;
 
           case $LT:
@@ -668,6 +705,10 @@
             handleTypeArguments(parser, stack);
             break;
 
+          case $AMPERSAND:
+            handleExtendedOperations(parser, stack);
+            break;
+
           default:
             JS('', 'throw "Bad character " + #', ch);
         }
@@ -727,6 +768,19 @@
     }
   }
 
+  static void handleExtendedOperations(Object parser, Object stack) {
+    var top = pop(stack);
+    if (0 == top) {
+      push(stack, _Universe._lookupNeverRti(universe(parser)));
+      return;
+    }
+    if (1 == top) {
+      push(stack, _Universe._lookupAnyRti(universe(parser)));
+      return;
+    }
+    throw AssertionError('Unexpected extended operation $top');
+  }
+
   static Object collectArray(Object parser, Object stack) {
     var array = _Utils.arraySplice(stack, position(parser));
     toTypes(_Parser.universe(parser), environment(parser), array);
@@ -796,6 +850,7 @@
       (ch == $$);
 
   static const int $$ = 0x24;
+  static const int $AMPERSAND = 0x26;
   static const int $PLUS = 0x2B;
   static const int $COMMA = 0x2C;
   static const int $PERIOD = 0x2E;
@@ -813,6 +868,7 @@
   static const int $a = $A + 32;
   static const int $z = $Z + 32;
   static const int $_ = 0x5F;
+  static const int $TILDE = 0x7E;
 }
 
 // -------- Subtype tests ------------------------------------------------------
diff --git a/tests/compiler/dart2js_extra/rti/simple_2_test.dart b/tests/compiler/dart2js_extra/rti/simple_2_test.dart
index ce81c7b..e092311 100644
--- a/tests/compiler/dart2js_extra/rti/simple_2_test.dart
+++ b/tests/compiler/dart2js_extra/rti/simple_2_test.dart
@@ -8,25 +8,72 @@
 testDynamic1() {
   var universe = rti.testingCreateUniverse();
 
-  var dynamicRti1 = rti.testingUniverseEval(universe, 'dynamic');
-  var dynamicRti2 = rti.testingUniverseEval(universe, ',,dynamic,,');
+  var rti1 = rti.testingUniverseEval(universe, 'dynamic');
+  var rti2 = rti.testingUniverseEval(universe, ',,dynamic,,');
 
-  Expect.isTrue(
-      identical(dynamicRti1, dynamicRti2), 'dynamic should be identical');
-  Expect.isFalse(dynamicRti1 is String);
-  Expect.equals('dynamic', rti.testingRtiToString(dynamicRti1));
+  Expect.isTrue(identical(rti1, rti2), 'dynamic should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('dynamic', rti.testingRtiToString(rti1));
 }
 
 testDynamic2() {
   var universe = rti.testingCreateUniverse();
 
-  var dynamicRti1 = rti.testingUniverseEval(universe, 'dynamic');
-  var dynamicRti2 = rti.testingUniverseEval(universe, ',,@,,');
+  var rti1 = rti.testingUniverseEval(universe, 'dynamic');
+  var rti2 = rti.testingUniverseEval(universe, ',,@,,');
 
-  Expect.isTrue(
-      identical(dynamicRti1, dynamicRti2), 'dynamic should be identical');
-  Expect.isFalse(dynamicRti1 is String);
-  Expect.equals('dynamic', rti.testingRtiToString(dynamicRti1));
+  Expect.isTrue(identical(rti1, rti2), 'dynamic should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('dynamic', rti.testingRtiToString(rti1));
+}
+
+testVoid() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '~');
+  var rti2 = rti.testingUniverseEval(universe, ',,~,,');
+
+  Expect.isTrue(identical(rti1, rti2), 'void should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('void', rti.testingRtiToString(rti1));
+}
+
+testNever() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '0&');
+  var rti2 = rti.testingUniverseEval(universe, '0&');
+
+  Expect.isTrue(identical(rti1, rti2), 'Never should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('Never', rti.testingRtiToString(rti1));
+}
+
+testAny() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '1&');
+  var rti2 = rti.testingUniverseEval(universe, '1&');
+
+  Expect.isTrue(identical(rti1, rti2), "'any' should be identical");
+  Expect.isFalse(rti1 is String);
+  Expect.equals('any', rti.testingRtiToString(rti1));
+}
+
+testTerminal() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '@');
+  var rti2 = rti.testingUniverseEval(universe, '~');
+  var rti3 = rti.testingUniverseEval(universe, '0&');
+  var rti4 = rti.testingUniverseEval(universe, '1&');
+
+  Expect.isFalse(identical(rti1, rti2));
+  Expect.isFalse(identical(rti1, rti3));
+  Expect.isFalse(identical(rti1, rti4));
+  Expect.isFalse(identical(rti2, rti3));
+  Expect.isFalse(identical(rti2, rti4));
+  Expect.isFalse(identical(rti3, rti4));
 }
 
 testInterface1() {
@@ -76,6 +123,10 @@
 main() {
   testDynamic1();
   testDynamic2();
+  testVoid();
+  testNever();
+  testAny();
+  testTerminal();
   testInterface1();
   testInterface2();
   testInterface3();