Skip to content

Commit e6d50af

Browse files
In the Canonical ABI, disallow empty types. (#218)
* In the Canonical ABI, disallow empty types. In C++, all types must have a non-zero size, so empty structs have size 1. In C, structs with no members are non-standard, though widely supported, with size 0, making them ABI-incompatible with C++. In Go, the spec says "Two distinct zero-size variables may have the same address in memory", and as a user, my understanding is the fact that it says "may" means one isn't guaranteed it's always one way or the other. To avoid these complexities, for now, disallow empty record and flags types. * Commented out empty record and flags tests. * Require `flags`, `record`, and `tuple` to be non-empty. * Update design/mvp/Binary.md Co-authored-by: Luke Wagner <[email protected]> * Update design/mvp/Binary.md Co-authored-by: Luke Wagner <[email protected]> * Update design/mvp/canonical-abi/definitions.py Co-authored-by: Luke Wagner <[email protected]> * Update design/mvp/CanonicalABI.md Co-authored-by: Luke Wagner <[email protected]> * Update design/mvp/canonical-abi/definitions.py Co-authored-by: Luke Wagner <[email protected]> * Update design/mvp/CanonicalABI.md Co-authored-by: Luke Wagner <[email protected]> --------- Co-authored-by: Luke Wagner <[email protected]>
1 parent d1d52de commit e6d50af

File tree

5 files changed

+29
-21
lines changed

5 files changed

+29
-21
lines changed

design/mvp/Binary.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ primvaltype ::= 0x7f => bool
196196
| 0x74 => char
197197
| 0x73 => string
198198
defvaltype ::= pvt:<primvaltype> => pvt
199-
| 0x72 lt*:vec(<labelvaltype>) => (record (field lt)*)
199+
| 0x72 lt*:vec(<labelvaltype>) => (record (field lt)*) (if |lt*| > 0)
200200
| 0x71 case*:vec(<case>) => (variant case*)
201201
| 0x70 t:<valtype> => (list t)
202-
| 0x6f t*:vec(<valtype>) => (tuple t*)
203-
| 0x6e l*:vec(<label>) => (flags l*)
202+
| 0x6f t*:vec(<valtype>) => (tuple t+) (if |t*| > 0)
203+
| 0x6e l*:vec(<label>) => (flags l+) (if |l*| > 0)
204204
| 0x6d l*:vec(<label>) => (enum l*)
205205
| 0x6c t*:vec(<valtype>) => (union t*)
206206
| 0x6b t:<valtype> => (option t)

design/mvp/CanonicalABI.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ below.
161161
### Size
162162

163163
Each value type is also assigned a `size`, measured in bytes, which corresponds
164-
the `sizeof` operator in C:
164+
the `sizeof` operator in C. Empty types, such as records with no fields, are
165+
not permitted, to avoid complications in source languages.
165166
```python
166167
def size(t):
167168
match despecialize(t):
@@ -184,6 +185,7 @@ def size_record(fields):
184185
for f in fields:
185186
s = align_to(s, alignment(f.t))
186187
s += size(f.t)
188+
assert(s > 0)
187189
return align_to(s, alignment_record(fields))
188190

189191
def align_to(ptr, alignment):
@@ -201,7 +203,7 @@ def size_variant(cases):
201203

202204
def size_flags(labels):
203205
n = len(labels)
204-
if n == 0: return 0
206+
assert(n > 0)
205207
if n <= 8: return 1
206208
if n <= 16: return 2
207209
return 4 * num_i32_flags(labels)

design/mvp/Explainer.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -487,11 +487,11 @@ defvaltype ::= bool
487487
| s8 | u8 | s16 | u16 | s32 | u32 | s64 | u64
488488
| float32 | float64
489489
| char | string
490-
| (record (field <label> <valtype>)*)
490+
| (record (field <label> <valtype>)+)
491491
| (variant (case <id>? <label> <valtype>? (refines <id>)?)+)
492492
| (list <valtype>)
493-
| (tuple <valtype>*)
494-
| (flags <label>*)
493+
| (tuple <valtype>+)
494+
| (flags <label>+)
495495
| (enum <label>+)
496496
| (union <valtype>+)
497497
| (option <valtype>)

design/mvp/canonical-abi/definitions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ def size_record(fields):
249249
for f in fields:
250250
s = align_to(s, alignment(f.t))
251251
s += size(f.t)
252+
assert(s > 0)
252253
return align_to(s, alignment_record(fields))
253254

254255
def align_to(ptr, alignment):
@@ -266,7 +267,7 @@ def size_variant(cases):
266267

267268
def size_flags(labels):
268269
n = len(labels)
269-
if n == 0: return 0
270+
assert(n > 0)
270271
if n <= 8: return 1
271272
if n <= 16: return 2
272273
return 4 * num_i32_flags(labels)

design/mvp/canonical-abi/run_tests.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,13 @@ def test_name():
9696
if not equal_modulo_string_encoding(got, lower_v):
9797
fail("{} re-lift expected {} but got {}".format(test_name(), lower_v, got))
9898

99-
test(Record([]), [], {})
99+
# Empty record types are not permitted yet.
100+
#test(Record([]), [], {})
100101
test(Record([Field('x',U8()), Field('y',U16()), Field('z',U32())]), [1,2,3], {'x':1,'y':2,'z':3})
101102
test(Tuple([Tuple([U8(),U8()]),U8()]), [1,2,3], {'0':{'0':1,'1':2},'1':3})
102-
t = Flags([])
103-
test(t, [], {})
103+
# Empty flags types are not permitted yet.
104+
#t = Flags([])
105+
#test(t, [], {})
104106
t = Flags(['a','b'])
105107
test(t, [0], {'a':False,'b':False})
106108
test(t, [2], {'a':False,'b':True})
@@ -242,7 +244,8 @@ def test_heap(t, expect, args, byte_array):
242244
cx = mk_cx(heap.memory)
243245
test(t, args, expect, cx)
244246

245-
test_heap(List(Record([])), [{},{},{}], [0,3], [])
247+
# Empty record types are not permitted yet.
248+
#test_heap(List(Record([])), [{},{},{}], [0,3], [])
246249
test_heap(List(Bool()), [True,False,True], [0,3], [1,0,1])
247250
test_heap(List(Bool()), [True,False,True], [0,3], [1,0,2])
248251
test_heap(List(Bool()), [True,False,True], [3,3], [0xff,0xff,0xff, 1,0,1])
@@ -276,21 +279,23 @@ def test_heap(t, expect, args, byte_array):
276279
[6,0, 7, 0x0ff, 8,0, 9, 0xff])
277280
test_heap(List(Tuple([Tuple([U16(),U8()]),U8()])), [mk_tup([4,5],6),mk_tup([7,8],9)], [0,2],
278281
[4,0, 5,0xff, 6,0xff, 7,0, 8,0xff, 9,0xff])
279-
test_heap(List(Union([Record([]),U8(),Tuple([U8(),U16()])])), [{'0':{}}, {'1':42}, {'2':mk_tup(6,7)}], [0,3],
280-
[0,0xff,0xff,0xff,0xff,0xff, 1,0xff,42,0xff,0xff,0xff, 2,0xff,6,0xff,7,0])
282+
# Empty record types are not permitted yet.
283+
#test_heap(List(Union([Record([]),U8(),Tuple([U8(),U16()])])), [{'0':{}}, {'1':42}, {'2':mk_tup(6,7)}], [0,3],
284+
# [0,0xff,0xff,0xff,0xff,0xff, 1,0xff,42,0xff,0xff,0xff, 2,0xff,6,0xff,7,0])
281285
test_heap(List(Union([U32(),U8()])), [{'0':256}, {'1':42}], [0,2],
282286
[0,0xff,0xff,0xff,0,1,0,0, 1,0xff,0xff,0xff,42,0xff,0xff,0xff])
283287
test_heap(List(Tuple([Union([U8(),Tuple([U16(),U8()])]),U8()])),
284288
[mk_tup({'1':mk_tup(5,6)},7),mk_tup({'0':8},9)], [0,2],
285289
[1,0xff,5,0,6,0xff,7,0xff, 0,0xff,8,0xff,0xff,0xff,9,0xff])
286290
test_heap(List(Union([U8()])), [{'0':6},{'0':7},{'0':8}], [0,3],
287291
[0,6, 0,7, 0,8])
288-
t = List(Flags([]))
289-
test_heap(t, [{},{},{}], [0,3],
290-
[])
291-
t = List(Tuple([Flags([]), U8()]))
292-
test_heap(t, [mk_tup({}, 42), mk_tup({}, 43), mk_tup({}, 44)], [0,3],
293-
[42,43,44])
292+
# Empty flags types are not permitted yet.
293+
#t = List(Flags([]))
294+
#test_heap(t, [{},{},{}], [0,3],
295+
# [])
296+
#t = List(Tuple([Flags([]), U8()]))
297+
#test_heap(t, [mk_tup({}, 42), mk_tup({}, 43), mk_tup({}, 44)], [0,3],
298+
# [42,43,44])
294299
t = List(Flags(['a','b']))
295300
test_heap(t, [{'a':False,'b':False},{'a':False,'b':True},{'a':True,'b':True}], [0,3],
296301
[0,2,3])

0 commit comments

Comments
 (0)