Skip to content

Commit d47441e

Browse files
authored
feat: Infer invariant when bound is isolated (no proper super/subtypes except Top/Bottom) (#5359)
1 parent ddf4351 commit d47441e

File tree

7 files changed

+226
-8
lines changed

7 files changed

+226
-8
lines changed

src/mo_frontend/bi_match.ml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,18 @@ let choose_under_constrained ctx lb c ub =
138138
| Variance.Contravariant -> ub
139139
| Variance.Bivariant -> lb
140140
| Variance.Invariant ->
141-
raise (Bimatch (Format.asprintf
142-
"implicit instantiation of type parameter %s is under-constrained with%a\nwhere%a\nso that explicit type instantiation is required"
143-
(Cons.name c)
144-
display_constraint (lb, c, ub)
145-
display_rel (lb,"=/=",ub)))
141+
match normalize lb, normalize ub with
142+
(* Ignore [Any] when choosing a bound for the solution *)
143+
(* Restrict to [isolated] types only, at least for now *)
144+
| t, Any when isolated t ->
145+
assert (t <> Non);
146+
lb
147+
| _ ->
148+
raise (Bimatch (Format.asprintf
149+
"implicit instantiation of type parameter %s is under-constrained with%a\nwhere%a\nso that explicit type instantiation is required"
150+
(Cons.name c)
151+
display_constraint (lb, c, ub)
152+
display_rel (lb,"=/=",ub)))
146153

147154
let fail_over_constrained lb c ub =
148155
raise (Bimatch (Format.asprintf

src/mo_types/type.ml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,51 @@ and singleton_field co tf = singleton_typ co tf.typ
14791479

14801480
and singleton t : bool = singleton_typ (ref S.empty) t
14811481

1482+
(** A type is isolated if it has no proper supertypes nor proper subtypes (ignoring top [Any] and bottom [Non]). *)
1483+
let rec isolated_typ co = function
1484+
| Con (c, ts) as t ->
1485+
(match Cons.kind c with
1486+
| Abs (_, bound) ->
1487+
(* Type parameters are isolated when unbounded. *)
1488+
bound = Any
1489+
| Def (tbs, def) ->
1490+
(* Cyclic definitions are isolated until proven otherwise. *)
1491+
S.mem t !co || begin
1492+
co := S.add t !co;
1493+
isolated_typ co (reduce tbs def ts)
1494+
end
1495+
)
1496+
| Mut _ -> true
1497+
| Named (_, t) -> isolated_typ co t
1498+
| Prim
1499+
( Bool
1500+
| Nat8
1501+
| Nat16
1502+
| Nat32
1503+
| Nat64
1504+
| Int8
1505+
| Int16
1506+
| Int32
1507+
| Int64
1508+
| Float
1509+
| Char
1510+
| Text
1511+
| Blob
1512+
| Error
1513+
| Principal
1514+
| Region
1515+
(* All except Nat, Int, Null as they have proper super/subtypes: Nat <: Int, ?T <: Null *)
1516+
) -> true
1517+
| Array t
1518+
| Async (_, _, t)
1519+
| Weak t -> isolated_typ co t
1520+
| Tup ts -> List.for_all (isolated_typ co) ts
1521+
| Func (_, _, _, ts1, ts2) ->
1522+
List.for_all (isolated_typ co) ts1 &&
1523+
List.for_all (isolated_typ co) ts2
1524+
| _ -> false
1525+
1526+
and isolated t = isolated_typ (ref S.empty) t
14821527

14831528
(* Least upper bound and greatest lower bound *)
14841529

src/mo_types/type.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ val stable : typ -> bool
224224

225225
val inhabited : typ -> bool
226226
val singleton : typ -> bool
227+
228+
(** A type is isolated if it has no proper supertypes nor proper subtypes (ignoring top [Any] and bottom [Non]). *)
229+
val isolated : typ -> bool
227230
val span : typ -> int option
228231

229232

test/fail/lambdas-core.mo

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import Prim "mo:prim";
22
func fail<T>() : T = Prim.trap("fail");
33
func check<T>(t1 : T, t2 : T) : [T] = [t1, t2]; // used to check type equality
4+
func check3<T>(t1 : T, t2 : T, t3 : T) : [T] = [t1, t2, t3]; // used to check type equality
5+
6+
type Order = { #less; #equal; #greater };
47

58
// Mock functions for Array module
69
module Array {
@@ -221,6 +224,7 @@ module Map {
221224
public func all<K, V>(_map : Map<K, V>, _predicate : (K, V) -> Bool) : Bool = true;
222225
public func any<K, V>(_map : Map<K, V>, _predicate : (K, V) -> Bool) : Bool = false;
223226
public func toText<K, V>(_map : Map<K, V>, _keyToText : K -> Text, _valueToText : V -> Text) : Text = "";
227+
public func fromIter<K, V>(_iter : Iter<(K, V)>, _compare : (K, K) -> Order) : Map<K, V> = fail();
224228
};
225229

226230
// Mock functions for pure Map module
@@ -405,19 +409,25 @@ let _ = Set.retainAll(set, natCompare, func n = n % 2 == 0);
405409
let _ = Set.forEach(set, func _ {});
406410
let _ = Set.filter(set, natCompare, func n = n % 2 == 0);
407411
let s1 = Set.map<Nat, Text>(set, func n = natToText(n));
412+
let s1i = Set.map(set, func n = natToText(n));
408413
let s2 : Set<Text> = Set.map(set, func n = natToText(n));
409-
let _ = check(s1, s2);
414+
let _ = check3(s1, s1i, s2);
410415
let s3 = Set.filterMap<Nat, Text>(
411416
set,
412417
textCompare,
413418
func n = if (n % 2 == 0) ?natToText(n) else null,
414419
);
420+
let s3i = Set.filterMap(
421+
set,
422+
textCompare,
423+
func n = if (n % 2 == 0) ?natToText(n) else null,
424+
);
415425
let s4 : Set<Text> = Set.filterMap(
416426
set,
417427
textCompare,
418428
func n = if (n % 2 == 0) ?natToText(n) else null,
419429
);
420-
let _ = check(s3, s4);
430+
let _ = check3(s3, s3i, s4);
421431
let _ = Set.all(set, func n = n < 10);
422432
let _ = Set.any(set, func n = n > 5);
423433

@@ -445,11 +455,13 @@ let _ = PureSet.any(pureSet, func n = n > 5);
445455
let _ = Map.forEach(mapInstance, func(key, value) {});
446456
let _ = Map.filter(mapInstance, natCompare, func(key, value) = key % 2 == 0);
447457
let m1 = Map.map<Nat, Text, Text>(mapInstance, func(key, value) = natToText(key));
458+
let m1i = Map.map(mapInstance, func(key, value) = natToText(key));
448459
let m2 : Map<Nat, Text> = Map.map(mapInstance, func(key, value) = natToText(key));
449-
let _ = check(m1, m2);
460+
let _ = check3(m1, m1i, m2);
450461
let _ = Map.all(mapInstance, func(k, v) = v == natToText(k));
451462
let _ = Map.any(mapInstance, func(k, v) = k >= 0);
452463
let _ = Map.toText(mapInstance, natToText, func t = t);
464+
let _ = Map.fromIter([(0, "0")].values(), natCompare);
453465

454466
// pure Map module explicit type instantiation tests
455467
let _ = PureMap.all(pureMap, func(k, v) = v == natToText(k));

test/fail/lambdas-invariant.mo

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Prim "mo:prim";
2+
3+
module VarArray {
4+
public func map<T, U>(_ : [var T], _ : (T) -> U) : [var U] = [var];
5+
};
6+
7+
let va = [var 1, 2, 3];
8+
9+
// Test that we can solve invariant U when the bound is an 'isolated' type, e.g.
10+
// Trying to solve: Bool <: U <: Any
11+
// Should pick U = Bool, because Bool has no proper supertypes/subtypes (except Any and Non)
12+
let _ = VarArray.map(va, func x = x % 2 == 0); // Bool
13+
let _ = VarArray.map(va, func x = Prim.natToNat8(x) + 1); // NatX
14+
let _ = VarArray.map(va, func x = Prim.nat64ToInt64(Prim.natToNat64(x)) - 1); // IntX
15+
let _ = VarArray.map(va, func x = x % 2 == 0); // Float
16+
let _ = VarArray.map(va, func x = Prim.nat32ToChar(Prim.natToNat32(x))); // Char
17+
let _ = VarArray.map(va, func x = debug_show (x)); // Text
18+
let _ = VarArray.map(va, func x = Prim.encodeUtf8(debug_show (x))); // Blob
19+
let _ = VarArray.map(va, func x = Prim.principalOfBlob(Prim.encodeUtf8(debug_show (x)))); // Principal
20+
let _ = VarArray.map(va, func x = [Prim.natToNat16(x)]); // [Nat16]
21+
let _ = VarArray.map(va, func x = [var x]); // [var Nat]
22+
let _ = VarArray.map(va, func x = (Prim.intToInt32(x), debug_show (x))); // (Int32, Text)
23+
let _ = VarArray.map(va, func x = func(y : Nat8) : Bool = Prim.natToNat8(x) == y); // Nat8 -> Bool
24+
25+
// Counter examples that should fail:
26+
func _m1() {
27+
let _ = VarArray.map(va, func x = x); // Nat
28+
};
29+
func _m2() {
30+
let _ = VarArray.map(va, func x = x : Int); // Int
31+
};
32+
func _m3() {
33+
let _ = VarArray.map(va, func x = null); // Null
34+
};
35+
func _m4() {
36+
let _ = VarArray.map(va, func x = ?Prim.natToNat8(x)); // ?Nat8
37+
};
38+
func _m5() {
39+
let _ = VarArray.map(va, func x = [x]); // [Nat]
40+
};
41+
func _m6() {
42+
let _ = VarArray.map(va, func x = { x }); // { x : Nat }
43+
};
44+
func _m7() {
45+
let _ = VarArray.map(va, func x = #c(x)); // { #c : Nat }
46+
};
47+
48+
// Termination check: should succeed
49+
type Bot = [Bot];
50+
let _ = VarArray.map(va, func x = [] : [Bot]); // [Bot]
51+
52+
// Type variables: isolated when unbounded
53+
func tvUnbounded<Ok>(t : Ok) {
54+
let _ = VarArray.map(va, func _ = t); // Ok
55+
};
56+
57+
func tvBoundedToIsolated<Er <: Text>(t : Er) {
58+
let _ = VarArray.map(va, func _ = t); // Er, should still fail because [var Er] =/= [var Text]
59+
};
60+
61+
func tvBoundedToNat<Er <: Nat>(t : Er) {
62+
let _ = VarArray.map(va, func _ = t); // Er, should fail
63+
};
64+
65+
//SKIP comp
66+
//SKIP run
67+
//SKIP run-drun
68+
//SKIP run-ir
69+
//SKIP run-low

test/fail/ok/lambdas-invariant.tc.ok

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
lambdas-invariant.mo:27.11-27.39: type error [M0098], cannot implicitly instantiate function of type
2+
<T, U>([var T], T -> U) -> [var U]
3+
to argument of type
4+
([var Nat], Nat -> U)
5+
because implicit instantiation of type parameter U is under-constrained with
6+
Nat <: U <: Any
7+
where
8+
Nat =/= Any
9+
so that explicit type instantiation is required
10+
lambdas-invariant.mo:30.11-30.45: type error [M0098], cannot implicitly instantiate function of type
11+
<T, U>([var T], T -> U) -> [var U]
12+
to argument of type
13+
([var Nat], Nat -> U)
14+
because implicit instantiation of type parameter U is under-constrained with
15+
Int <: U <: Any
16+
where
17+
Int =/= Any
18+
so that explicit type instantiation is required
19+
lambdas-invariant.mo:33.11-33.42: type error [M0098], cannot implicitly instantiate function of type
20+
<T, U>([var T], T -> U) -> [var U]
21+
to argument of type
22+
([var Nat], Nat -> U)
23+
because implicit instantiation of type parameter U is under-constrained with
24+
Null <: U <: Any
25+
where
26+
Null =/= Any
27+
so that explicit type instantiation is required
28+
lambdas-invariant.mo:36.11-36.56: type error [M0098], cannot implicitly instantiate function of type
29+
<T, U>([var T], T -> U) -> [var U]
30+
to argument of type
31+
([var Nat], Nat -> U)
32+
because implicit instantiation of type parameter U is under-constrained with
33+
?Nat8 <: U <: Any
34+
where
35+
?Nat8 =/= Any
36+
so that explicit type instantiation is required
37+
lambdas-invariant.mo:39.11-39.41: type error [M0098], cannot implicitly instantiate function of type
38+
<T, U>([var T], T -> U) -> [var U]
39+
to argument of type
40+
([var Nat], Nat -> U)
41+
because implicit instantiation of type parameter U is under-constrained with
42+
[Nat] <: U <: Any
43+
where
44+
[Nat] =/= Any
45+
so that explicit type instantiation is required
46+
lambdas-invariant.mo:42.11-42.43: type error [M0098], cannot implicitly instantiate function of type
47+
<T, U>([var T], T -> U) -> [var U]
48+
to argument of type
49+
([var Nat], Nat -> U)
50+
because implicit instantiation of type parameter U is under-constrained with
51+
{x : Nat} <: U <: Any
52+
where
53+
{x : Nat} =/= Any
54+
so that explicit type instantiation is required
55+
lambdas-invariant.mo:45.11-45.43: type error [M0098], cannot implicitly instantiate function of type
56+
<T, U>([var T], T -> U) -> [var U]
57+
to argument of type
58+
([var Nat], Nat -> U)
59+
because implicit instantiation of type parameter U is under-constrained with
60+
{#c : Nat} <: U <: Any
61+
where
62+
{#c : Nat} =/= Any
63+
so that explicit type instantiation is required
64+
lambdas-invariant.mo:58.11-58.39: type error [M0098], cannot implicitly instantiate function of type
65+
<T, U>([var T], T -> U) -> [var U]
66+
to argument of type
67+
([var Nat], Nat -> U)
68+
because implicit instantiation of type parameter U is under-constrained with
69+
Er <: U <: Any
70+
where
71+
Er =/= Any
72+
so that explicit type instantiation is required
73+
lambdas-invariant.mo:62.11-62.39: type error [M0098], cannot implicitly instantiate function of type
74+
<T, U>([var T], T -> U) -> [var U]
75+
to argument of type
76+
([var Nat], Nat -> U)
77+
because implicit instantiation of type parameter U is under-constrained with
78+
Er <: U <: Any
79+
where
80+
Er =/= Any
81+
so that explicit type instantiation is required
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Return code 1

0 commit comments

Comments
 (0)