Skip to content

Language-agnostic types schema #282

Description

@cameel

The format/type schema currently seems strongly tied to the range of types available in Classic Solidity. I think it needs to be a bit more generic to accommodate types from languages with different or more flexible type systems. Even for Classic Solidity it's a bit limiting because it provides too little flexibility for defining new types even if they are simple variations of existing ones. If we keep it this way, it will have to be updated with each new language supporting it and also along with evolution of the languages that already do. It would be much better if the languages were free to change type representations without requiring changes to the spec each time.

Example

More concretely, the problem is that the current spec does not provide properties for exhaustively describing the types it has, instead implicitly assuming Solidity's choices. For example:

  • {"kind": "int", "bits": 16} is specifically a big-endian, right-aligned, two's complement signed 16-bit integer. Different alignment cannot be expressed.
  • {"kind": "address"} is a left-aligned, sequence of 20 bytes. There is no way to describe an address that always takes a full 32-byte slot (which we are planning to introduce in the near future).
  • {"kind": "array", "count": 10, "contains": {"type": {"kind": "int80"}}} is an array that packs three elements per slot in storage (left-aligned), but 1 element per slot in memory (right-aligned). There is no way to have an array that is not packed in storage (and Core Solidity provides non-packed types; in fact it does not support packed ones yet).
  • {"kind": "function", "internal": false} is a combination of a 20-byte address and a 4-byte selector. Packed into a single slot in calldata, but taking two separate slots on the stack. Again, no way to express different packing or address size.
  • {"kind": "string"} is a sequence of (variable-length) UTF-8 characters and a length expressed in bytes.

In some cases these are EVM's design choices (e.g. the endianness and negative integer representation) and it's unlikely for a language to diverge from them (though not impossible), but others (e.g. alignment, packing and character encoding) are not universal. The latter should have corresponding properties in the spec.

Proposed solution

There are two aspects to the current types, which I think should be decoupled:

  • Interpretation of the value: whether it should be presented as a number, byte, character, address, collection, function, etc. This is how a human interprets it.
  • Encoding of the value: byte representation of the value and its placement in the region of memory assigned to it.

I think that types should map to interpretations and properties should define the encoding. For example I'd introduce a single "Number" type for all kinds of numbers (integers/fractions, signed/unsigned, fixed-/floating-point). It should have properties expressing the encoding: size, alignment, signedness and precision. int16 could then be described as:

{
    "kind": "number",
    "representation": "fixed-point",
    "alignment": "right",
    "signed": true,
    "bits": 16,
    "decimals": 0
}

In some cases where there are only minor differences between interpretations, we could have them covered by a single type. For example an addresses and a contract is almost the same thing. Both can be payable or not, both are internally an address, both will need to be extended to 32 bytes in the future. The difference is just that a contract type has a definition. We could cover both with an address type with an optional definition property.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions