Google is committed to advancing racial equity for Black communities. See how.

Change a union from flexible to strict

Overview

- init step 1 step 2 step 3
fidl link link
dart link link link
go link link
hlcpp link link link
llcpp link link link
rust link link link

Initial State

FIDL

flexible union JsonValue {
    1: int32 int_value;
    2: string:MAX string_value;
};

Dart

void useUnion(fidllib.JsonValue value) {
  switch (value.$tag) {
    case fidllib.JsonValueTag.intValue:
      print('int value: ${value.intValue}');
      break;
    case fidllib.JsonValueTag.stringValue:
      print('string value: ${value.stringValue}');
      break;
    case fidllib.JsonValueTag.$unknown:
      print('unknown variant: ${value.$unknownData}');
      break;
  }
}

Go

func useUnion(value lib.JsonValue) {
    switch value.Which() {
    case lib.JsonValueIntValue:
        fmt.Printf("int value: %d\n", value.IntValue)
    case lib.JsonValueStringValue:
        fmt.Printf("string value: %s\n", value.StringValue)
    case lib.JsonValue_unknownData:
        fmt.Printf("unknown data: %+v\n", value.GetUnknownData())
    default:
        fmt.Println("<unknown tag>")
    }
}

HLCPP

void use_union(fidl_test::JsonValue value) {
  switch (value.Which()) {
    case fidl_test::JsonValue::Tag::kIntValue:
      printf("int value: %d\n", value.int_value());
      break;
    case fidl_test::JsonValue::Tag::kStringValue:
      printf("string value: %s\n", value.string_value().c_str());
      break;
    case fidl_test::JsonValue::Tag::Invalid:
      printf("<uninitialized union>\n");
      break;
    case fidl_test::JsonValue::Tag::kUnknown:
      printf("<%lu unknown bytes>\n", value.UnknownBytes()->size());
      break;
  }
}

LLCPP

void use_union(fidl_test::JsonValue* value) {
  switch (value->which()) {
    case fidl_test::JsonValue::Tag::kIntValue:
      printf("int value: %d\n", value->int_value());
      break;
    case fidl_test::JsonValue::Tag::kStringValue:
      printf("string value: %s\n", value->string_value().data());
      break;
    case fidl_test::JsonValue::Tag::kUnknown:
      printf("<unknown data>\n");
      break;
  }
}

Rust

fn use_union(value: &fidl_lib::JsonValue) {
    match value {
        fidl_lib::JsonValue::IntValue(n) => println!("int value: {}", n),
        fidl_lib::JsonValue::StringValue(s) => println!("string: {}", s),
        fidl_lib::JsonValueUnknown!() => println!("<unknown union>"),
    };
}

Update Source Code

Dart

  • Replace the unknown tag with a default case in any switch statements
  void useUnion(fidllib.JsonValue value) {
    switch (value.$tag) {
      case fidllib.JsonValueTag.intValue:
        print('int value: ${value.intValue}');
        break;
      case fidllib.JsonValueTag.stringValue:
        print('string value: ${value.stringValue}');
        break;
-     case fidllib.JsonValueTag.$unknown:
+     default:
+       // Note: unknown variants will fail to decode until the union is marked flexible
        print('unknown variant: ${value.$unknownData}');
        break;
    }
  }
  

Go

  • Remove usages of any flexible union specific APIs
  func useUnion(value lib.JsonValue) {
    switch value.Which() {
    case lib.JsonValueIntValue:
        fmt.Printf("int value: %d\n", value.IntValue)
    case lib.JsonValueStringValue:
        fmt.Printf("string value: %s\n", value.StringValue)
-   case lib.JsonValue_unknownData:
-       fmt.Printf("unknown data: %+v\n", value.GetUnknownData())
    default:
        fmt.Println("<unknown tag>")
    }
  }
  

HLCPP

  • Replace the kUnknown tag with a default case in any switch statements
  • Remove usages of any flexible union specific APIs
  void use_union(fidl_test::JsonValue value) {
    switch (value.Which()) {
      case fidl_test::JsonValue::Tag::kIntValue:
        printf("int value: %d\n", value.int_value());
        break;
      case fidl_test::JsonValue::Tag::kStringValue:
        printf("string value: %s\n", value.string_value().c_str());
        break;
      case fidl_test::JsonValue::Tag::Invalid:
        printf("<uninitialized union>\n");
        break;
-     case fidl_test::JsonValue::Tag::kUnknown:
-       printf("<%lu unknown bytes>\n", value.UnknownBytes()->size());
-       break;
+     default:
+       printf("<unknown variant>\n");
    }
  }
  

LLCPP

  • Replace the kUnknown tag with a default case in any switch statements
  • Remove usages of any flexible union specific APIs
  void use_union(fidl_test::JsonValue* value) {
    switch (value->which()) {
      case fidl_test::JsonValue::Tag::kIntValue:
        printf("int value: %d\n", value->int_value());
        break;
      case fidl_test::JsonValue::Tag::kStringValue:
        printf("string value: %s\n", value->string_value().data());
        break;
-     case fidl_test::JsonValue::Tag::kUnknown:
-       printf("<unknown data>\n");
-       break;
+     default:
+       printf("<unknown variant>\n");
    }
  }

Rust

  • Add [allow(unreachable_patterns)] and an underscore arm to match statements on the union
  fn use_union(value: &fidl_lib::JsonValue) {
+     #[allow(unreachable_patterns)]
      match value {
          fidl_lib::JsonValue::IntValue(n) => println!("int value: {}", n),
          fidl_lib::JsonValue::StringValue(s) => println!("string: {}", s),
-         fidl_lib::JsonValueUnknown!() => println!("<unknown union>"),
+         _ => {}
      };
  }

Update FIDL Library

  • Change the union from flexible to strict
- flexible union JsonValue {
+ strict union JsonValue {
      1: int32 int_value;
      2: string:MAX string_value;
  };

Update Source Code

Dart

  • You can now remove the default case in any switch statements
  void useUnion(fidllib.JsonValue value) {
+   assert(value.$unknownData == null);
    switch (value.$tag) {
      case fidllib.JsonValueTag.intValue:
        print('int value: ${value.intValue}');
        break;
      case fidllib.JsonValueTag.stringValue:
        print('string value: ${value.stringValue}');
        break;
-     default:
-       // Note: unknown variants will fail to decode until the union is marked flexible
-       print('unknown variant: ${value.$unknownData}');
-       break;
    }
  }
  

HLCPP

  • You can now remove the default case
  void use_union(fidl_test::JsonValue value) {
    switch (value.Which()) {
      case fidl_test::JsonValue::Tag::kIntValue:
        printf("int value: %d\n", value.int_value());
        break;
      case fidl_test::JsonValue::Tag::kStringValue:
        printf("string value: %s\n", value.string_value().c_str());
        break;
      case fidl_test::JsonValue::Tag::Invalid:
        printf("<uninitialized union>\n");
        break;
-     default:
-       printf("<unknown variant>\n");
    }
  }
  

LLCPP

  • You can now remove the default case
  void use_union(fidl_test::JsonValue* value) {
    switch (value->which()) {
      case fidl_test::JsonValue::Tag::kIntValue:
        printf("int value: %d\n", value->int_value());
        break;
      case fidl_test::JsonValue::Tag::kStringValue:
        printf("string value: %s\n", value->string_value().data());
        break;
-     default:
-       printf("<unknown variant>\n");
    }
  }

Rust

  • Remove the attribute and underscore arm
  fn use_union(value: &fidl_lib::JsonValue) {
-     #[allow(unreachable_patterns)]
      match value {
          fidl_lib::JsonValue::IntValue(n) => println!("int value: {}", n),
          fidl_lib::JsonValue::StringValue(s) => println!("string: {}", s),
-         _ => {}
      };
  }