Skip to content

Commit a56d7ad

Browse files
authored
Merge pull request #6128 from roc-lang/debug-auto-opaque
Make sure late specializations of opaques inherit Inspect as needed
2 parents 7d2b8a5 + a53da2b commit a56d7ad

File tree

5 files changed

+286
-91
lines changed

5 files changed

+286
-91
lines changed

crates/compiler/load_internal/src/file.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -4662,7 +4662,9 @@ pub fn add_imports(
46624662

46634663
let mut cached_symbol_vars = VecMap::default();
46644664

4665-
for &symbol in &exposed_for_module.imported_values {
4665+
let imported_values = exposed_for_module.imported_values.iter().copied();
4666+
4667+
for symbol in imported_values {
46664668
import_variable_for_symbol(
46674669
subs,
46684670
constraints,

crates/compiler/solve/src/specialize.rs

+130-90
Original file line numberDiff line numberDiff line change
@@ -723,115 +723,155 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
723723
phase: &P,
724724
ability_member: Symbol,
725725
lset_region: u8,
726-
specialization_key: SpecializationTypeKey,
726+
mut specialization_key: SpecializationTypeKey,
727727
target_rank: Rank,
728728
) -> Result<Variable, ()> {
729-
match specialization_key {
730-
SpecializationTypeKey::Opaque(opaque) => {
731-
let opaque_home = opaque.module_id();
732-
let external_specialized_lset =
733-
phase.with_module_abilities_store(opaque_home, |abilities_store| {
734-
let impl_key = roc_can::abilities::ImplKey {
729+
loop {
730+
match specialization_key {
731+
SpecializationTypeKey::Opaque(opaque) => {
732+
let opaque_home = opaque.module_id();
733+
let found = phase.with_module_abilities_store(opaque_home, |abilities_store| {
734+
find_opaque_specialization_ambient_function(
735+
abilities_store,
735736
opaque,
736737
ability_member,
737-
};
738-
739-
let opt_specialization =
740-
abilities_store.get_implementation(impl_key);
741-
match opt_specialization {
742-
None => {
743-
if P::IS_LATE {
744-
internal_error!(
745-
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
746-
opaque,
747-
ability_member
748-
);
749-
} else {
750-
// doesn't specialize, we'll have reported an error for this
751-
Err(())
752-
}
738+
lset_region,
739+
)
740+
});
741+
742+
let external_specialized_lset = match found {
743+
FoundOpaqueSpecialization::UpdatedSpecializationKey(key) => {
744+
specialization_key = key;
745+
continue;
746+
}
747+
FoundOpaqueSpecialization::AmbientFunction(lset) => lset,
748+
FoundOpaqueSpecialization::NotFound => {
749+
if P::IS_LATE {
750+
internal_error!(
751+
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
752+
opaque,
753+
ability_member
754+
);
755+
} else {
756+
// We'll have reported an error for this.
757+
return Err(());
753758
}
754-
Some(member_impl) => match member_impl {
755-
MemberImpl::Impl(spec_symbol) => {
756-
let specialization =
757-
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
758-
759-
let specialized_lambda_set = *specialization
760-
.specialization_lambda_sets
761-
.get(&lset_region)
762-
.unwrap_or_else(|| panic!("lambda set region not resolved: {:?}", (spec_symbol, specialization)));
763-
Ok(specialized_lambda_set)
764-
}
765-
MemberImpl::Error => todo_abilities!(),
766-
},
767759
}
768-
})?;
760+
};
769761

770-
let specialized_ambient = phase.copy_lambda_set_ambient_function_to_home_subs(
771-
external_specialized_lset,
772-
opaque_home,
773-
subs,
774-
);
762+
let specialized_ambient = phase.copy_lambda_set_ambient_function_to_home_subs(
763+
external_specialized_lset,
764+
opaque_home,
765+
subs,
766+
);
775767

776-
Ok(specialized_ambient)
777-
}
768+
return Ok(specialized_ambient);
769+
}
778770

779-
SpecializationTypeKey::Derived(derive_key) => {
780-
let mut derived_module = derived_env.derived_module.lock().unwrap();
771+
SpecializationTypeKey::Derived(derive_key) => {
772+
let mut derived_module = derived_env.derived_module.lock().unwrap();
781773

782-
let (_, _, specialization_lambda_sets) =
783-
derived_module.get_or_insert(derived_env.exposed_types, derive_key);
774+
let (_, _, specialization_lambda_sets) =
775+
derived_module.get_or_insert(derived_env.exposed_types, derive_key);
784776

785-
let specialized_lambda_set = *specialization_lambda_sets
786-
.get(&lset_region)
787-
.expect("lambda set region not resolved");
777+
let specialized_lambda_set = *specialization_lambda_sets
778+
.get(&lset_region)
779+
.expect("lambda set region not resolved");
788780

789-
let specialized_ambient = derived_module.copy_lambda_set_ambient_function_to_subs(
790-
specialized_lambda_set,
791-
subs,
792-
target_rank,
793-
);
781+
let specialized_ambient = derived_module.copy_lambda_set_ambient_function_to_subs(
782+
specialized_lambda_set,
783+
subs,
784+
target_rank,
785+
);
794786

795-
Ok(specialized_ambient)
796-
}
787+
return Ok(specialized_ambient);
788+
}
797789

798-
SpecializationTypeKey::Immediate(imm) => {
799-
// Immediates are like opaques in that we can simply look up their type definition in
800-
// the ability store, there is nothing new to synthesize.
801-
//
802-
// THEORY: if something can become an immediate, it will always be available in the
803-
// local ability store, because the transformation is local (?)
804-
//
805-
// TODO: I actually think we can get what we need here by examining `derived_env.exposed_types`,
806-
// since immediates can only refer to builtins - and in userspace, all builtin types
807-
// are available in `exposed_types`.
808-
let immediate_lambda_set_at_region =
809-
phase.get_and_copy_ability_member_ambient_function(imm, lset_region, subs);
810-
811-
Ok(immediate_lambda_set_at_region)
812-
}
790+
SpecializationTypeKey::Immediate(imm) => {
791+
// Immediates are like opaques in that we can simply look up their type definition in
792+
// the ability store, there is nothing new to synthesize.
793+
//
794+
// THEORY: if something can become an immediate, it will always be available in the
795+
// local ability store, because the transformation is local (?)
796+
//
797+
// TODO: I actually think we can get what we need here by examining `derived_env.exposed_types`,
798+
// since immediates can only refer to builtins - and in userspace, all builtin types
799+
// are available in `exposed_types`.
800+
let immediate_lambda_set_at_region =
801+
phase.get_and_copy_ability_member_ambient_function(imm, lset_region, subs);
802+
803+
return Ok(immediate_lambda_set_at_region);
804+
}
813805

814-
SpecializationTypeKey::SingleLambdaSetImmediate(imm) => {
815-
let module_id = imm.module_id();
816-
debug_assert!(module_id.is_builtin());
806+
SpecializationTypeKey::SingleLambdaSetImmediate(imm) => {
807+
let module_id = imm.module_id();
808+
debug_assert!(module_id.is_builtin());
817809

818-
let module_types = &derived_env
819-
.exposed_types
820-
.get(&module_id)
821-
.unwrap()
822-
.exposed_types_storage_subs;
810+
let module_types = &derived_env
811+
.exposed_types
812+
.get(&module_id)
813+
.unwrap()
814+
.exposed_types_storage_subs;
823815

824-
// Since this immediate has only one lambda set, the region must be pointing to 1, and
825-
// moreover the imported function type is the ambient function of the single lset.
826-
debug_assert_eq!(lset_region, 1);
827-
let storage_var = module_types.stored_vars_by_symbol.get(&imm).unwrap();
828-
let imported = module_types
829-
.storage_subs
830-
.export_variable_to(subs, *storage_var);
816+
// Since this immediate has only one lambda set, the region must be pointing to 1, and
817+
// moreover the imported function type is the ambient function of the single lset.
818+
debug_assert_eq!(lset_region, 1);
819+
let storage_var = module_types.stored_vars_by_symbol.get(&imm).unwrap();
820+
let imported = module_types
821+
.storage_subs
822+
.export_variable_to(subs, *storage_var);
831823

832-
roc_types::subs::instantiate_rigids(subs, imported.variable);
824+
roc_types::subs::instantiate_rigids(subs, imported.variable);
833825

834-
Ok(imported.variable)
826+
return Ok(imported.variable);
827+
}
835828
}
836829
}
837830
}
831+
832+
enum FoundOpaqueSpecialization {
833+
UpdatedSpecializationKey(SpecializationTypeKey),
834+
AmbientFunction(Variable),
835+
NotFound,
836+
}
837+
838+
fn find_opaque_specialization_ambient_function(
839+
abilities_store: &AbilitiesStore,
840+
opaque: Symbol,
841+
ability_member: Symbol,
842+
lset_region: u8,
843+
) -> FoundOpaqueSpecialization {
844+
let impl_key = roc_can::abilities::ImplKey {
845+
opaque,
846+
ability_member,
847+
};
848+
849+
let opt_specialization = abilities_store.get_implementation(impl_key);
850+
match opt_specialization {
851+
None => match ability_member {
852+
Symbol::INSPECT_TO_INSPECTOR => FoundOpaqueSpecialization::UpdatedSpecializationKey(
853+
SpecializationTypeKey::Immediate(Symbol::INSPECT_OPAQUE),
854+
),
855+
_ => FoundOpaqueSpecialization::NotFound,
856+
},
857+
Some(member_impl) => match member_impl {
858+
MemberImpl::Impl(spec_symbol) => {
859+
let specialization =
860+
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
861+
862+
let specialized_lambda_set = *specialization
863+
.specialization_lambda_sets
864+
.get(&lset_region)
865+
.unwrap_or_else(|| {
866+
panic!(
867+
"lambda set region not resolved: {:?}",
868+
(spec_symbol, specialization)
869+
)
870+
});
871+
872+
FoundOpaqueSpecialization::AmbientFunction(specialized_lambda_set)
873+
}
874+
MemberImpl::Error => todo_abilities!(),
875+
},
876+
}
877+
}

crates/compiler/test_gen/src/gen_abilities.rs

+38
Original file line numberDiff line numberDiff line change
@@ -2288,4 +2288,42 @@ mod inspect {
22882288
RocStr
22892289
);
22902290
}
2291+
2292+
#[test]
2293+
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
2294+
fn opaque_automatic() {
2295+
assert_evals_to!(
2296+
indoc!(
2297+
r#"
2298+
app "test" provides [main] to "./platform"
2299+
2300+
Op := {}
2301+
2302+
main = Inspect.toDbgStr (Inspect.inspect (@Op {}))
2303+
"#
2304+
),
2305+
RocStr::from(r#"<opaque>"#),
2306+
RocStr
2307+
);
2308+
}
2309+
2310+
#[test]
2311+
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
2312+
fn opaque_automatic_with_polymorphic_call() {
2313+
assert_evals_to!(
2314+
indoc!(
2315+
r#"
2316+
app "test" provides [main] to "./platform"
2317+
2318+
Op := {}
2319+
2320+
late = \a -> Inspect.toDbgStr (Inspect.inspect a)
2321+
2322+
main = late (@Op {})
2323+
"#
2324+
),
2325+
RocStr::from(r#"<opaque>"#),
2326+
RocStr
2327+
);
2328+
}
22912329
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# +emit:mono
2+
app "test" provides [main] to "./platform"
3+
4+
Op := {}
5+
6+
main =
7+
dbg (@Op {})
8+
1
9+
10+
# -emit:mono
11+
procedure Inspect.251 (Inspect.252):
12+
let Inspect.317 : Str = "<opaque>";
13+
let Inspect.316 : Str = CallByName Inspect.61 Inspect.252 Inspect.317;
14+
ret Inspect.316;
15+
16+
procedure Inspect.30 (Inspect.147):
17+
ret Inspect.147;
18+
19+
procedure Inspect.35 (Inspect.300):
20+
ret Inspect.300;
21+
22+
procedure Inspect.36 (Inspect.304):
23+
let Inspect.311 : Str = "";
24+
ret Inspect.311;
25+
26+
procedure Inspect.45 (Inspect.302):
27+
let Inspect.314 : {} = Struct {};
28+
let Inspect.313 : {} = CallByName Inspect.30 Inspect.314;
29+
ret Inspect.313;
30+
31+
procedure Inspect.5 (Inspect.150):
32+
let Inspect.312 : {} = CallByName Inspect.45 Inspect.150;
33+
let Inspect.309 : {} = Struct {};
34+
let Inspect.308 : Str = CallByName Inspect.36 Inspect.309;
35+
let Inspect.307 : Str = CallByName Inspect.251 Inspect.308;
36+
ret Inspect.307;
37+
38+
procedure Inspect.61 (Inspect.303, Inspect.298):
39+
let Inspect.319 : Str = CallByName Str.3 Inspect.303 Inspect.298;
40+
dec Inspect.298;
41+
ret Inspect.319;
42+
43+
procedure Str.3 (#Attr.2, #Attr.3):
44+
let Str.292 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
45+
ret Str.292;
46+
47+
procedure Test.0 ():
48+
let Test.5 : {} = Struct {};
49+
let Test.4 : Str = CallByName Inspect.5 Test.5;
50+
let Test.2 : Str = CallByName Inspect.35 Test.4;
51+
dbg Test.2;
52+
dec Test.2;
53+
let Test.3 : I64 = 1i64;
54+
ret Test.3;

0 commit comments

Comments
 (0)