Skip to content

Revise rules for type conversion of struct values #914

@bocchino

Description

@bocchino

Currently when we convert a struct value v to a struct type T, any members of T that are missing in v get filled in with the default values of the member types. For example, in the definition

struct S { x: U32, y: F32 } default { x = 5 }

when we compute the default value of S, we convert the specified default value { x = 5 } to { x = 5, y = 0.0 } at type S, because 0.0 is the default value of the type F32. This rule has worked well so far, because the only place where we currently do this conversion is in computing default values of struct definitions (as in the example above), and this is the rule we want in that case.

However, @Kronos3 has pointed out that it's not an ideal rule once we introduce configuration parameters in templates. For example, if we had this configuration type

struct Config {
  config1: U32
  config2: F32
} default {
  config1 = 5
  config2 = 10.0
}

and we had a configuration parameter p of type Config, it would be nice to be able to bind { config1 = 3 } to p and have it become

{ config1 = 3, config2 = 10.0 }

instead of the current rule, which produces { config1 = 3, config2 = 0.0 }. That would allow us to use all the default values except for ones that are explicitly overridden.

We can specify this behavior as follows.

Type conversion: We can amend § 21.2.8 of the spec (https://nasa.github.io/fpp/fpp-spec.html#Evaluation_Type-Conversion_Structure-Values) to say that when we are converting a struct value with missing members to a struct type T, we use the corresponding members in the default value of T. For example, if the model contains this struct definition

constant c = { x = 1 }

struct S {
  x: U32
  y: F32
} default c

then converting {} to type S yields { x = 1, y = 0.0 }.

Computing default values in struct definitions: Section 5.13.2 of the spec (https://nasa.github.io/fpp/fpp-spec.html#Definitions_Struct-Definitions_Semantics) says that when computing the default value of a struct type T, we evaluate the specified default expression (if any) to a value v and then convert v to type T. Since converting to T now appeals to the default value of T, we will have to amend the specification to avoid circularity (appealing to the default value of T when constructing the default value of T). We can revise the algorithm as follows:

  1. Construct the “default default” value d of T. This is the value that is the default value of T if no default value is specified in the definition of T. It has the default value for each type at each member of T. (See https://nasa.github.io/fpp/fpp-spec.html#Types_Default-Values).
  2. If no default value expression e is specified in the definition of T, then use d as the default value of T. Otherwise (a) evaluate e to a value v; (b) convert v to the type of d yielding a value v'; and (c) convert v’ to the type of T yielding a value v’’. Use v’’ as the default value of T.

For example, for this struct definition

constant c = { x = 1 }

struct S {
  x: U32
  y: F32
} default c

we get the following:

  • d is { x = 0, y = 0.0 }
  • e is c
  • v is { x = 1 }
  • v’ is { x = 1, y = 0.0 }
  • v’’ is { x = 1, y = 0.0 } : S

There's no circularity in step 2(c) because there are no missing members in the value v'. So we don't need to appeal to the default value of T when converting v' to T. So we can do the conversion as part of computing the default value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    semanticsIssues related to semanticsspecIssues related to the FPP language specification

    Type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions