Skip to content

Commit 7be7eb0

Browse files
committed
Added details about bounds on type variables in structs and enums.
1 parent fcf52a2 commit 7be7eb0

File tree

1 file changed

+90
-4
lines changed

1 file changed

+90
-4
lines changed

0000-well-formed.md

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,107 @@
66

77
Check all types for well-formedness with respect to the bounds of type variables.
88

9+
Allow bounds on formal type variable in structs and enums. Check these bounds
10+
are satisfied wherever the struct or enum is used with actual type parameters.
11+
912
# Motivation
1013

11-
Makes type checking saner. Catches errors earlier in the development process. Matches behaviour with built-in bounds (I think).
14+
Makes type checking saner. Catches errors earlier in the development process.
15+
Matches behaviour with built-in bounds (I think).
16+
17+
Currently formal type variables in traits and functions may have bounds and
18+
these bounds are checked whenever the item is used against the actual type
19+
variables. Where these type variables are used in types, these types
20+
should be checked for well-formedness with respect to the type definitions.
21+
E.g.,
22+
23+
```
24+
trait U {}
25+
trait T<X: U> {}
26+
trait S<Y> {
27+
fn m(x: ~T<Y>) {} // Should be flagged as an error
28+
}
29+
```
30+
31+
Formal type variables in structs and enums may not have bounds. It is possible
32+
to use these type variables in the types of fields, and these types cannot be
33+
checked for well-formedness until the struct is instantiated, where each field
34+
must be checked.
35+
36+
```
37+
struct St<X> {
38+
f: ~T<X>, // Cannot be checked
39+
}
40+
```
41+
42+
Likewise, impls of structs are not checked. E.g.,
43+
44+
```
45+
impl<X> St<X> { // Cannot be checked
46+
...
47+
}
48+
```
49+
50+
Here, no struct can exist where `X` is replaced by something implementing `U`,
51+
so in the impl, `X` can be assumed to have the bound `U`. But the impl does not
52+
indicate this. Note, this is sound, but does not indicate programmer intent very
53+
well.
1254

1355
# Detailed design
1456

15-
Whenever a type is used it must be checked for well-formedness. For polymorphic types we currently check only that the type exists. I would like to also check that any actual type parameters are valid. That is, given a type `T<U>` where `T` is declared as `T<X: B>`, we currently only check that `T` does in fact exist somewhere (I think we also check that the correct number of type parameters are supplied, in this case one). I would also like to check that `U` satisfies the bound `B`.
57+
Whenever a type is used it must be checked for well-formedness. For polymorphic
58+
types we currently check only that the type exists. I would like to also check
59+
that any actual type parameters are valid. That is, given a type `T<U>` where
60+
`T` is declared as `T<X: B>`, we currently only check that `T` does in fact
61+
exist somewhere (I think we also check that the correct number of type
62+
parameters are supplied, in this case one). I would also like to check that `U`
63+
satisfies the bound `B`.
64+
65+
Work on built-in bounds is (I think) in the process of adding this behaviour for
66+
built-in bounds. I would like to apply this to user-specified bounds too.
67+
68+
I think no fewer programs can be expressed. That is, any errors we catch with
69+
this new check would have been caught later in the existing scheme, where
70+
exactly would depend on where the type was used. The only exception would be if
71+
the formal type variable was not used.
72+
73+
We would allow bounds on type variable in structs and enums. Wherever a concrete
74+
struct or enum type appears, check the actual type variables against the bounds
75+
on the formals (the type well-formedness check).
1676

17-
Work on built-in bounds is (I think) in the process of adding this behaviour for built-in bounds. I would like to apply this to user-specified bounds too.
77+
From the above examples:
1878

19-
I think no fewer programs can be expressed. That is, any errors we catch with this new check would have been caught later in the existing scheme, where exactly would depend on where the type was used. The only exception would be if the formal type variable was not used.
79+
'''
80+
trait U {}
81+
trait T<X: U> {}
82+
trait S1<Y> {
83+
fn m(x: ~T<Y>) {} //~ ERROR
84+
}
85+
trait S2<Y: U> {
86+
fn m(x: ~T<Y>) {}
87+
}
88+
89+
struct St<X: U> {
90+
f: ~T<X>,
91+
}
92+
93+
impl<X: U> St<X> {
94+
...
95+
}
96+
'''
2097

2198
# Alternatives
2299

23100
Keep the status quo.
24101

102+
We could add bounds on structs, etc. But not check them in impls. This is safe
103+
since the implementation is more general than the struct. It would mean we allow
104+
impls to be un-necessarily general.
105+
25106
# Unresolved questions
26107

108+
Do we allow and check bounds in type aliases? We currently do not. We should
109+
probably continue not to since these type variables (and indeed the type
110+
aliases) are substituted away early in the type checking process. So if we think
111+
of type aliases as almost macro-like, then not checking makes sense. OTOH, it is
112+
still a little bit inconsistent.

0 commit comments

Comments
 (0)