Skip to content

Commit 8d01bfc

Browse files
committed
Don't allow inline types to subtype traits
This fixes a bug in the type checker that would incorrectly allow inline types to subtype trait values. For example, code such as this was allowed: type inline Stream[T] { ... } trait Foo { fn iter -> Iter[Int] } type inline Bar {} impl Foo for Bar { fn iter -> Stream[Int] { ... } } This isn't correct because traits used as values are boxed and addressed through a pointer, which isn't the case for inline types. Changelog: fixed
1 parent 4f08289 commit 8d01bfc

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
trait Iter[T] {}
2+
3+
trait Range {
4+
fn iter -> Iter[Int]
5+
}
6+
7+
type HeapStream[T] {}
8+
9+
impl Iter[T] for HeapStream {}
10+
11+
type inline InlineStream[T] {}
12+
13+
impl Iter[T] for InlineStream {}
14+
15+
type Range1 {}
16+
17+
impl Range for Range1 {
18+
fn iter -> InlineStream[Int] {
19+
InlineStream()
20+
}
21+
}
22+
23+
type Range2 {}
24+
25+
impl Range for Range2 {
26+
fn iter -> Iter[Int] {
27+
HeapStream() as Iter[Int]
28+
}
29+
}
30+
31+
# implement_method_that_returns_inline_type_instead_of_trait.inko:18:3 error(invalid-method): the method 'fn iter -> InlineStream[Int]' isn't compatible with 'fn iter -> Iter[Int]'

std/src/std/range.inko

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import std.clone (Clone)
2424
import std.cmp (Contains, Equal)
2525
import std.fmt (Format, Formatter)
2626
import std.hash (Hash, Hasher)
27-
import std.iter (Iter, Stream)
27+
import std.iter (Stream)
2828

2929
# A range of integers.
3030
trait pub Range: Contains[Int] + Hash + Format {
@@ -41,7 +41,7 @@ trait pub Range: Contains[Int] + Hash + Format {
4141
fn pub size -> Int
4242

4343
# Returns an iterator over the values in `self`.
44-
fn pub iter -> Iter[Int]
44+
fn pub iter -> Stream[Int]
4545
}
4646

4747
# An inclusive range of integers.

std/src/std/string.inko

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ trait pub Bytes {
172172
fn pub byte(index: Int) -> Int
173173

174174
# Returns an iterator over the bytes in `self`.
175-
fn pub bytes -> Iter[Int]
175+
fn pub bytes -> Stream[Int]
176176

177177
# Returns the number of bytes in `self`.
178178
fn pub size -> Int

types/src/check.rs

+20-17
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use std::collections::HashSet;
88
#[derive(Copy, Clone)]
99
enum Subtyping {
1010
No,
11-
Yes,
12-
Once,
11+
Relaxed,
12+
Strict,
1313
}
1414

1515
#[derive(Copy, Clone)]
@@ -75,7 +75,7 @@ impl Rules {
7575
}
7676

7777
fn without_subtyping(mut self) -> Rules {
78-
if let Subtyping::Yes = self.subtyping {
78+
if let Subtyping::Relaxed = self.subtyping {
7979
self.subtyping = Subtyping::No
8080
}
8181

@@ -97,13 +97,13 @@ impl Rules {
9797
self
9898
}
9999

100-
fn with_one_time_subtyping(mut self) -> Rules {
101-
self.subtyping = Subtyping::Once;
100+
fn with_strict_subtyping(mut self) -> Rules {
101+
self.subtyping = Subtyping::Strict;
102102
self
103103
}
104104

105-
fn with_subtyping(mut self) -> Rules {
106-
self.subtyping = Subtyping::Yes;
105+
fn with_relaxed_subtyping(mut self) -> Rules {
106+
self.subtyping = Subtyping::Relaxed;
107107
self
108108
}
109109

@@ -214,8 +214,7 @@ impl<'a> TypeChecker<'a> {
214214
let mut env =
215215
Environment::new(left.type_arguments(db), right.type_arguments(db));
216216

217-
let rules =
218-
Rules::new().with_kind(Kind::Cast).with_one_time_subtyping();
217+
let rules = Rules::new().with_kind(Kind::Cast).with_strict_subtyping();
219218

220219
TypeChecker::new(db).check_type_ref(left, right, &mut env, rules)
221220
}
@@ -321,7 +320,7 @@ impl<'a> TypeChecker<'a> {
321320
lhs.return_type,
322321
rhs.return_type,
323322
env,
324-
rules.with_subtyping(),
323+
rules.with_strict_subtyping(),
325324
)
326325
}
327326

@@ -348,7 +347,7 @@ impl<'a> TypeChecker<'a> {
348347
env.left.assign(bound, val);
349348

350349
let mut env = env.with_left_as_right();
351-
let rules = Rules::new().with_subtyping();
350+
let rules = Rules::new().with_relaxed_subtyping();
352351

353352
if bound.is_mutable(self.db) && !val.allow_mutating(self.db) {
354353
return false;
@@ -703,7 +702,7 @@ impl<'a> TypeChecker<'a> {
703702
) -> bool {
704703
let trait_rules = rules;
705704

706-
if let Subtyping::Once = rules.subtyping {
705+
if let Subtyping::Strict = rules.subtyping {
707706
rules.subtyping = Subtyping::No;
708707
}
709708

@@ -772,7 +771,7 @@ impl<'a> TypeChecker<'a> {
772771
lhs,
773772
req,
774773
env,
775-
rules.with_one_time_subtyping(),
774+
rules.with_relaxed_subtyping(),
776775
)
777776
})
778777
}
@@ -963,7 +962,7 @@ impl<'a> TypeChecker<'a> {
963962

964963
// At this point no value is assigned yet, so it's safe to allow
965964
// sub-typing through traits.
966-
let rules = rules.with_one_time_subtyping();
965+
let rules = rules.with_relaxed_subtyping();
967966
let res = match left_id {
968967
TypeEnum::TypeInstance(lhs) => reqs
969968
.into_iter()
@@ -1000,7 +999,7 @@ impl<'a> TypeChecker<'a> {
1000999
TypeArguments::for_trait(self.db, right),
10011000
);
10021001

1003-
let rules = Rules::new().with_one_time_subtyping();
1002+
let rules = Rules::new().with_relaxed_subtyping();
10041003

10051004
self.check_type_with_trait(left, right, &mut env, rules)
10061005
}
@@ -1037,8 +1036,12 @@ impl<'a> TypeChecker<'a> {
10371036
// result in a `Dog` being added to the Array.
10381037
match rules.subtyping {
10391038
Subtyping::No => return false,
1040-
Subtyping::Yes => {}
1041-
Subtyping::Once => {
1039+
Subtyping::Relaxed => rules.subtyping = Subtyping::No,
1040+
Subtyping::Strict => {
1041+
if !left.instance_of.allow_cast_to_trait(self.db) {
1042+
return false;
1043+
}
1044+
10421045
rules.subtyping = Subtyping::No;
10431046
}
10441047
}

0 commit comments

Comments
 (0)