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

Change a union from strict to flexible

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

type JsonValue = strict union {
    1: int_value int32;
    2: string_value string:MAX;
};

Dart

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;
  }
}

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)
    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;
  }
}

LLCPP

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

Update Source Code

Dart

  • Add a default case to any switch statements on the union to handle new unknown variants
  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

  • Add a default case to any switch statements on the union to handle new unknown variants
  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

  • Add a default case to any switch statements on the union to handle new unknown variants
  void use_union(fidl_test::wire::JsonValue* value) {
    switch (value->which()) {
      case fidl_test::wire::JsonValue::Tag::kIntValue:
        printf("int value: %d\n", value->int_value());
        break;
      case fidl_test::wire::JsonValue::Tag::kStringValue:
        printf("string value: %s\n", value->string_value().data());
        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),
+         _ => {}
      };
  }

Update FIDL Library

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

Update Source Code

Dart

  • Replace the default case with the unknown tag.
  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;
-     default:
-       // Note: unknown variants will fail to decode until the union is marked flexible
+     case fidllib.JsonValueTag.$unknown:
        print('unknown variant: ${value.$unknownData}');
        break;
    }
  }
  

Go

  • You may now use 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 default case with the kUnknown tag.
  • You may now use 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;
-     default:
-       printf("<unknown variant>\n");
+     case fidl_test::JsonValue::Tag::kUnknown:
+       printf("<%lu unknown bytes>\n", value.UnknownBytes()->size());
+       break;
    }
  }
  

LLCPP

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

Rust

  • Remove the attribute and replace the underscore arm with the generated macro to match against unknown variants
  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>"),
      };
  }