Skip to content

Commit a19cd2c

Browse files
committed
Reintroduce Self types
This reintroduces Self types after they were removed as part of the 0.11.0 release. This new setup is much simpler, less buggy (in part due to that simplicity), and allows us to solve various soundness issues that the lack of Self types either introduced or made difficult to resolve otherwise. This fixes #643, fixes #787 and fixes #811. Changelog: added
1 parent a3a15ed commit a19cd2c

31 files changed

+999
-212
lines changed

compiler/src/hir.rs

+50-2
Large diffs are not rendered by default.

compiler/src/mir/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -2287,7 +2287,12 @@ impl Mir {
22872287
if let Some(stype) = tid.specialization_key(&state.db).self_type
22882288
{
22892289
let self_node = state.dependency_graph.add_module(
2290-
stype.instance_of().module(&state.db).name(&state.db),
2290+
stype
2291+
.as_type_instance()
2292+
.unwrap()
2293+
.instance_of()
2294+
.module(&state.db)
2295+
.name(&state.db),
22912296
);
22922297

22932298
state

compiler/src/mir/specialize.rs

+37-21
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ use indexmap::{IndexMap, IndexSet};
77
use std::collections::{HashMap, HashSet, VecDeque};
88
use std::mem::swap;
99
use types::check::TypeChecker;
10+
use types::format::format_type;
1011
use types::specialize::{ordered_shapes_from_map, TypeSpecializer};
1112
use types::{
1213
Block as _, Database, InternedTypeArguments, MethodId, Shape,
13-
TypeArguments, TypeId, TypeInstance, TypeParameterId, TypeRef, CALL_METHOD,
14-
DECREMENT_METHOD, DROPPER_METHOD, INCREMENT_METHOD,
14+
TypeArguments, TypeEnum, TypeId, TypeInstance, TypeParameterId, TypeRef,
15+
CALL_METHOD, DECREMENT_METHOD, DROPPER_METHOD, INCREMENT_METHOD,
1516
};
1617

1718
fn argument_shape(
@@ -34,7 +35,7 @@ fn specialize_constants(
3435

3536
// Constants never need access to the self type, so we just use a dummy
3637
// value here.
37-
let stype = TypeInstance::new(TypeId::nil());
38+
let stype = TypeEnum::TypeInstance(TypeInstance::new(TypeId::nil()));
3839

3940
for &id in mir.constants.keys() {
4041
let old_typ = id.value_type(db);
@@ -97,7 +98,7 @@ fn shapes_compatible_with_bounds(
9798

9899
struct Job {
99100
/// The type of `self` within the method.
100-
self_type: TypeInstance,
101+
self_type: TypeEnum,
101102

102103
/// The ID of the method that's being specialized.
103104
method: MethodId,
@@ -124,7 +125,7 @@ impl Work {
124125

125126
fn push(
126127
&mut self,
127-
self_type: TypeInstance,
128+
self_type: TypeEnum,
128129
method: MethodId,
129130
shapes: HashMap<TypeParameterId, Shape>,
130131
) -> bool {
@@ -228,7 +229,7 @@ impl DynamicCalls {
228229

229230
/// A compiler pass that specializes generic types.
230231
pub(crate) struct Specialize<'a, 'b> {
231-
self_type: TypeInstance,
232+
self_type: TypeEnum,
232233
method: MethodId,
233234
state: &'a mut State,
234235
work: &'b mut Work,
@@ -276,7 +277,11 @@ impl<'a, 'b> Specialize<'a, 'b> {
276277
let main_method = state.db.main_method().unwrap();
277278
let main_mod = main_type.module(&state.db);
278279

279-
work.push(TypeInstance::new(main_type), main_method, HashMap::new());
280+
work.push(
281+
TypeEnum::TypeInstance(TypeInstance::new(main_type)),
282+
main_method,
283+
HashMap::new(),
284+
);
280285

281286
// The main() method isn't called explicitly, so we have to manually
282287
// record it in the main type.
@@ -852,11 +857,20 @@ impl<'a, 'b> Specialize<'a, 'b> {
852857
type_arguments: Option<&TypeArguments>,
853858
) -> Instruction {
854859
let typ = receiver.instance_of();
855-
let method_impl = typ
860+
861+
let Some(method_impl) = typ
856862
.specialization_source(&self.state.db)
857863
.unwrap_or(typ)
858864
.method(&self.state.db, call.method.name(&self.state.db))
859-
.unwrap();
865+
else {
866+
panic!(
867+
"can't devirtualize call to {}.{} in {}.{}",
868+
receiver.instance_of().name(&self.state.db),
869+
call.method.name(&self.state.db),
870+
format_type(&self.state.db, self.self_type),
871+
self.method.name(&self.state.db),
872+
);
873+
};
860874

861875
let mut shapes = type_arguments
862876
.map(|args| self.type_argument_shapes(call.method, args))
@@ -899,18 +913,18 @@ impl<'a, 'b> Specialize<'a, 'b> {
899913
type_id: TypeId,
900914
method: MethodId,
901915
shapes: &HashMap<TypeParameterId, Shape>,
902-
custom_self_type: Option<TypeInstance>,
916+
custom_self_type: Option<TypeEnum>,
903917
) -> MethodId {
904918
let ins = TypeInstance::new(type_id);
905-
let stype = custom_self_type.unwrap_or(ins);
919+
let stype = custom_self_type.unwrap_or(TypeEnum::TypeInstance(ins));
906920

907921
// Regular methods on regular types don't need to be specialized.
908922
if !type_id.is_generic(&self.state.db)
909923
&& !type_id.is_closure(&self.state.db)
910924
&& !method.is_generic(&self.state.db)
911925
{
912926
if self.work.push(stype, method, shapes.clone()) {
913-
self.update_method_type(method, shapes);
927+
self.update_method_type(stype, method, shapes);
914928
self.regular_methods.push(method);
915929
}
916930

@@ -959,7 +973,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
959973

960974
for name in methods {
961975
let method = type_id.method(&self.state.db, name).unwrap();
962-
let stype = TypeInstance::new(type_id);
976+
let stype = TypeEnum::TypeInstance(TypeInstance::new(type_id));
963977

964978
if self.work.push(stype, method, HashMap::new()) {
965979
self.regular_methods.push(method);
@@ -980,7 +994,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
980994
let stype = if type_id.is_closure(&self.state.db) {
981995
self.self_type
982996
} else {
983-
TypeInstance::new(type_id)
997+
TypeEnum::TypeInstance(TypeInstance::new(type_id))
984998
};
985999

9861000
if original == type_id {
@@ -1021,7 +1035,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
10211035
let method = original.method(&self.state.db, name).unwrap();
10221036

10231037
if original == type_id {
1024-
let stype = TypeInstance::new(type_id);
1038+
let stype = TypeEnum::TypeInstance(TypeInstance::new(type_id));
10251039

10261040
if self.work.push(stype, method, HashMap::new()) {
10271041
self.regular_methods.push(method);
@@ -1089,14 +1103,15 @@ impl<'a, 'b> Specialize<'a, 'b> {
10891103

10901104
let new = method.clone_for_specialization(&mut self.state.db);
10911105
let old_ret = method.return_type(&self.state.db);
1106+
let stype = receiver.as_type_enum(&self.state.db).unwrap();
10921107

10931108
for arg in method.arguments(&self.state.db) {
10941109
let arg_type = TypeSpecializer::new(
10951110
&mut self.state.db,
10961111
self.interned,
10971112
shapes,
10981113
&mut self.types,
1099-
self.self_type,
1114+
stype,
11001115
)
11011116
.specialize(arg.value_type);
11021117

@@ -1107,7 +1122,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
11071122
self.interned,
11081123
shapes,
11091124
&mut self.types,
1110-
self.self_type,
1125+
stype,
11111126
)
11121127
.specialize(raw_var_type);
11131128

@@ -1125,7 +1140,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
11251140
self.interned,
11261141
shapes,
11271142
&mut self.types,
1128-
self.self_type,
1143+
stype,
11291144
)
11301145
.specialize(old_ret);
11311146

@@ -1150,6 +1165,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
11501165
/// return types are specialized (if needed).
11511166
fn update_method_type(
11521167
&mut self,
1168+
self_type: TypeEnum,
11531169
method: MethodId,
11541170
shapes: &HashMap<TypeParameterId, Shape>,
11551171
) {
@@ -1161,7 +1177,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
11611177
self.interned,
11621178
shapes,
11631179
&mut self.types,
1164-
self.self_type,
1180+
self_type,
11651181
)
11661182
.specialize(arg.value_type);
11671183

@@ -1171,7 +1187,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
11711187
self.interned,
11721188
shapes,
11731189
&mut self.types,
1174-
self.self_type,
1190+
self_type,
11751191
)
11761192
.specialize(raw_var_type);
11771193

@@ -1189,7 +1205,7 @@ impl<'a, 'b> Specialize<'a, 'b> {
11891205
self.interned,
11901206
shapes,
11911207
&mut self.types,
1192-
self.self_type,
1208+
self_type,
11931209
)
11941210
.specialize(old_ret);
11951211

compiler/src/symbol_names.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ fn format_type_base_name(db: &Database, id: TypeId, name: &mut String) {
7878
}
7979

8080
let loc = id.location(db);
81-
let stype = id.specialization_key(db).self_type.unwrap().instance_of();
81+
let stype = id
82+
.specialization_key(db)
83+
.self_type
84+
.and_then(|e| e.as_type_instance())
85+
.unwrap()
86+
.instance_of();
8287

8388
// The exact format used here doesn't really matter, but we try to keep it
8489
// somewhat readable for use in external tooling (e.g. a profiler that

compiler/src/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub(crate) fn hir_type_name(
4545
location: Location,
4646
) -> hir::TypeName {
4747
hir::TypeName {
48+
self_type: false,
4849
source: None,
4950
resolved_type: TypeRef::Unknown,
5051
name: hir::Constant { name: name.to_string(), location },

compiler/src/type_check/define_types.rs

-15
Original file line numberDiff line numberDiff line change
@@ -1875,21 +1875,6 @@ mod tests {
18751875
assert_eq!(error.location(), &cols(33, 46));
18761876
}
18771877

1878-
#[test]
1879-
fn test_define_field_with_self_type() {
1880-
let mut state = State::new(Config::new());
1881-
let mut modules = parse(&mut state, "type Person { let @name: Self }");
1882-
1883-
DefineTypes::run_all(&mut state, &mut modules);
1884-
1885-
assert!(!DefineFields::run_all(&mut state, &mut modules));
1886-
1887-
let error = state.diagnostics.iter().next().unwrap();
1888-
1889-
assert_eq!(error.id(), DiagnosticId::InvalidSymbol);
1890-
assert_eq!(error.location(), &cols(26, 29));
1891-
}
1892-
18931878
#[test]
18941879
fn test_define_trait_type_parameter() {
18951880
let mut state = State::new(Config::new());

0 commit comments

Comments
 (0)