mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
Merge pull request #6128 from roc-lang/debug-auto-opaque
Make sure late specializations of opaques inherit Inspect as needed
This commit is contained in:
commit
a56d7adc17
5 changed files with 285 additions and 90 deletions
|
@ -4662,7 +4662,9 @@ pub fn add_imports(
|
||||||
|
|
||||||
let mut cached_symbol_vars = VecMap::default();
|
let mut cached_symbol_vars = VecMap::default();
|
||||||
|
|
||||||
for &symbol in &exposed_for_module.imported_values {
|
let imported_values = exposed_for_module.imported_values.iter().copied();
|
||||||
|
|
||||||
|
for symbol in imported_values {
|
||||||
import_variable_for_symbol(
|
import_variable_for_symbol(
|
||||||
subs,
|
subs,
|
||||||
constraints,
|
constraints,
|
||||||
|
|
|
@ -723,115 +723,155 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
||||||
phase: &P,
|
phase: &P,
|
||||||
ability_member: Symbol,
|
ability_member: Symbol,
|
||||||
lset_region: u8,
|
lset_region: u8,
|
||||||
specialization_key: SpecializationTypeKey,
|
mut specialization_key: SpecializationTypeKey,
|
||||||
target_rank: Rank,
|
target_rank: Rank,
|
||||||
) -> Result<Variable, ()> {
|
) -> Result<Variable, ()> {
|
||||||
match specialization_key {
|
loop {
|
||||||
SpecializationTypeKey::Opaque(opaque) => {
|
match specialization_key {
|
||||||
let opaque_home = opaque.module_id();
|
SpecializationTypeKey::Opaque(opaque) => {
|
||||||
let external_specialized_lset =
|
let opaque_home = opaque.module_id();
|
||||||
phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
let found = phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
||||||
let impl_key = roc_can::abilities::ImplKey {
|
find_opaque_specialization_ambient_function(
|
||||||
|
abilities_store,
|
||||||
opaque,
|
opaque,
|
||||||
ability_member,
|
ability_member,
|
||||||
};
|
lset_region,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let opt_specialization =
|
let external_specialized_lset = match found {
|
||||||
abilities_store.get_implementation(impl_key);
|
FoundOpaqueSpecialization::UpdatedSpecializationKey(key) => {
|
||||||
match opt_specialization {
|
specialization_key = key;
|
||||||
None => {
|
continue;
|
||||||
if P::IS_LATE {
|
|
||||||
internal_error!(
|
|
||||||
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
|
|
||||||
opaque,
|
|
||||||
ability_member
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// doesn't specialize, we'll have reported an error for this
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(member_impl) => match member_impl {
|
|
||||||
MemberImpl::Impl(spec_symbol) => {
|
|
||||||
let specialization =
|
|
||||||
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
|
|
||||||
|
|
||||||
let specialized_lambda_set = *specialization
|
|
||||||
.specialization_lambda_sets
|
|
||||||
.get(&lset_region)
|
|
||||||
.unwrap_or_else(|| panic!("lambda set region not resolved: {:?}", (spec_symbol, specialization)));
|
|
||||||
Ok(specialized_lambda_set)
|
|
||||||
}
|
|
||||||
MemberImpl::Error => todo_abilities!(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})?;
|
FoundOpaqueSpecialization::AmbientFunction(lset) => lset,
|
||||||
|
FoundOpaqueSpecialization::NotFound => {
|
||||||
|
if P::IS_LATE {
|
||||||
|
internal_error!(
|
||||||
|
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
|
||||||
|
opaque,
|
||||||
|
ability_member
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// We'll have reported an error for this.
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let specialized_ambient = phase.copy_lambda_set_ambient_function_to_home_subs(
|
let specialized_ambient = phase.copy_lambda_set_ambient_function_to_home_subs(
|
||||||
external_specialized_lset,
|
external_specialized_lset,
|
||||||
opaque_home,
|
opaque_home,
|
||||||
subs,
|
subs,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(specialized_ambient)
|
return Ok(specialized_ambient);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpecializationTypeKey::Derived(derive_key) => {
|
SpecializationTypeKey::Derived(derive_key) => {
|
||||||
let mut derived_module = derived_env.derived_module.lock().unwrap();
|
let mut derived_module = derived_env.derived_module.lock().unwrap();
|
||||||
|
|
||||||
let (_, _, specialization_lambda_sets) =
|
let (_, _, specialization_lambda_sets) =
|
||||||
derived_module.get_or_insert(derived_env.exposed_types, derive_key);
|
derived_module.get_or_insert(derived_env.exposed_types, derive_key);
|
||||||
|
|
||||||
let specialized_lambda_set = *specialization_lambda_sets
|
let specialized_lambda_set = *specialization_lambda_sets
|
||||||
.get(&lset_region)
|
.get(&lset_region)
|
||||||
.expect("lambda set region not resolved");
|
.expect("lambda set region not resolved");
|
||||||
|
|
||||||
let specialized_ambient = derived_module.copy_lambda_set_ambient_function_to_subs(
|
let specialized_ambient = derived_module.copy_lambda_set_ambient_function_to_subs(
|
||||||
specialized_lambda_set,
|
specialized_lambda_set,
|
||||||
subs,
|
subs,
|
||||||
target_rank,
|
target_rank,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(specialized_ambient)
|
return Ok(specialized_ambient);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpecializationTypeKey::Immediate(imm) => {
|
SpecializationTypeKey::Immediate(imm) => {
|
||||||
// Immediates are like opaques in that we can simply look up their type definition in
|
// Immediates are like opaques in that we can simply look up their type definition in
|
||||||
// the ability store, there is nothing new to synthesize.
|
// the ability store, there is nothing new to synthesize.
|
||||||
//
|
//
|
||||||
// THEORY: if something can become an immediate, it will always be available in the
|
// THEORY: if something can become an immediate, it will always be available in the
|
||||||
// local ability store, because the transformation is local (?)
|
// local ability store, because the transformation is local (?)
|
||||||
//
|
//
|
||||||
// TODO: I actually think we can get what we need here by examining `derived_env.exposed_types`,
|
// TODO: I actually think we can get what we need here by examining `derived_env.exposed_types`,
|
||||||
// since immediates can only refer to builtins - and in userspace, all builtin types
|
// since immediates can only refer to builtins - and in userspace, all builtin types
|
||||||
// are available in `exposed_types`.
|
// are available in `exposed_types`.
|
||||||
let immediate_lambda_set_at_region =
|
let immediate_lambda_set_at_region =
|
||||||
phase.get_and_copy_ability_member_ambient_function(imm, lset_region, subs);
|
phase.get_and_copy_ability_member_ambient_function(imm, lset_region, subs);
|
||||||
|
|
||||||
Ok(immediate_lambda_set_at_region)
|
return Ok(immediate_lambda_set_at_region);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpecializationTypeKey::SingleLambdaSetImmediate(imm) => {
|
SpecializationTypeKey::SingleLambdaSetImmediate(imm) => {
|
||||||
let module_id = imm.module_id();
|
let module_id = imm.module_id();
|
||||||
debug_assert!(module_id.is_builtin());
|
debug_assert!(module_id.is_builtin());
|
||||||
|
|
||||||
let module_types = &derived_env
|
let module_types = &derived_env
|
||||||
.exposed_types
|
.exposed_types
|
||||||
.get(&module_id)
|
.get(&module_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.exposed_types_storage_subs;
|
.exposed_types_storage_subs;
|
||||||
|
|
||||||
// Since this immediate has only one lambda set, the region must be pointing to 1, and
|
// Since this immediate has only one lambda set, the region must be pointing to 1, and
|
||||||
// moreover the imported function type is the ambient function of the single lset.
|
// moreover the imported function type is the ambient function of the single lset.
|
||||||
debug_assert_eq!(lset_region, 1);
|
debug_assert_eq!(lset_region, 1);
|
||||||
let storage_var = module_types.stored_vars_by_symbol.get(&imm).unwrap();
|
let storage_var = module_types.stored_vars_by_symbol.get(&imm).unwrap();
|
||||||
let imported = module_types
|
let imported = module_types
|
||||||
.storage_subs
|
.storage_subs
|
||||||
.export_variable_to(subs, *storage_var);
|
.export_variable_to(subs, *storage_var);
|
||||||
|
|
||||||
roc_types::subs::instantiate_rigids(subs, imported.variable);
|
roc_types::subs::instantiate_rigids(subs, imported.variable);
|
||||||
|
|
||||||
Ok(imported.variable)
|
return Ok(imported.variable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FoundOpaqueSpecialization {
|
||||||
|
UpdatedSpecializationKey(SpecializationTypeKey),
|
||||||
|
AmbientFunction(Variable),
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_opaque_specialization_ambient_function(
|
||||||
|
abilities_store: &AbilitiesStore,
|
||||||
|
opaque: Symbol,
|
||||||
|
ability_member: Symbol,
|
||||||
|
lset_region: u8,
|
||||||
|
) -> FoundOpaqueSpecialization {
|
||||||
|
let impl_key = roc_can::abilities::ImplKey {
|
||||||
|
opaque,
|
||||||
|
ability_member,
|
||||||
|
};
|
||||||
|
|
||||||
|
let opt_specialization = abilities_store.get_implementation(impl_key);
|
||||||
|
match opt_specialization {
|
||||||
|
None => match ability_member {
|
||||||
|
Symbol::INSPECT_TO_INSPECTOR => FoundOpaqueSpecialization::UpdatedSpecializationKey(
|
||||||
|
SpecializationTypeKey::Immediate(Symbol::INSPECT_OPAQUE),
|
||||||
|
),
|
||||||
|
_ => FoundOpaqueSpecialization::NotFound,
|
||||||
|
},
|
||||||
|
Some(member_impl) => match member_impl {
|
||||||
|
MemberImpl::Impl(spec_symbol) => {
|
||||||
|
let specialization =
|
||||||
|
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
|
||||||
|
|
||||||
|
let specialized_lambda_set = *specialization
|
||||||
|
.specialization_lambda_sets
|
||||||
|
.get(&lset_region)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"lambda set region not resolved: {:?}",
|
||||||
|
(spec_symbol, specialization)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
FoundOpaqueSpecialization::AmbientFunction(specialized_lambda_set)
|
||||||
|
}
|
||||||
|
MemberImpl::Error => todo_abilities!(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2288,4 +2288,42 @@ mod inspect {
|
||||||
RocStr
|
RocStr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn opaque_automatic() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Op := {}
|
||||||
|
|
||||||
|
main = Inspect.toDbgStr (Inspect.inspect (@Op {}))
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from(r#"<opaque>"#),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn opaque_automatic_with_polymorphic_call() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Op := {}
|
||||||
|
|
||||||
|
late = \a -> Inspect.toDbgStr (Inspect.inspect a)
|
||||||
|
|
||||||
|
main = late (@Op {})
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from(r#"<opaque>"#),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
# +emit:mono
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Op := {}
|
||||||
|
|
||||||
|
main =
|
||||||
|
dbg (@Op {})
|
||||||
|
1
|
||||||
|
|
||||||
|
# -emit:mono
|
||||||
|
procedure Inspect.251 (Inspect.252):
|
||||||
|
let Inspect.317 : Str = "<opaque>";
|
||||||
|
let Inspect.316 : Str = CallByName Inspect.61 Inspect.252 Inspect.317;
|
||||||
|
ret Inspect.316;
|
||||||
|
|
||||||
|
procedure Inspect.30 (Inspect.147):
|
||||||
|
ret Inspect.147;
|
||||||
|
|
||||||
|
procedure Inspect.35 (Inspect.300):
|
||||||
|
ret Inspect.300;
|
||||||
|
|
||||||
|
procedure Inspect.36 (Inspect.304):
|
||||||
|
let Inspect.311 : Str = "";
|
||||||
|
ret Inspect.311;
|
||||||
|
|
||||||
|
procedure Inspect.45 (Inspect.302):
|
||||||
|
let Inspect.314 : {} = Struct {};
|
||||||
|
let Inspect.313 : {} = CallByName Inspect.30 Inspect.314;
|
||||||
|
ret Inspect.313;
|
||||||
|
|
||||||
|
procedure Inspect.5 (Inspect.150):
|
||||||
|
let Inspect.312 : {} = CallByName Inspect.45 Inspect.150;
|
||||||
|
let Inspect.309 : {} = Struct {};
|
||||||
|
let Inspect.308 : Str = CallByName Inspect.36 Inspect.309;
|
||||||
|
let Inspect.307 : Str = CallByName Inspect.251 Inspect.308;
|
||||||
|
ret Inspect.307;
|
||||||
|
|
||||||
|
procedure Inspect.61 (Inspect.303, Inspect.298):
|
||||||
|
let Inspect.319 : Str = CallByName Str.3 Inspect.303 Inspect.298;
|
||||||
|
dec Inspect.298;
|
||||||
|
ret Inspect.319;
|
||||||
|
|
||||||
|
procedure Str.3 (#Attr.2, #Attr.3):
|
||||||
|
let Str.292 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||||
|
ret Str.292;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.5 : {} = Struct {};
|
||||||
|
let Test.4 : Str = CallByName Inspect.5 Test.5;
|
||||||
|
let Test.2 : Str = CallByName Inspect.35 Test.4;
|
||||||
|
dbg Test.2;
|
||||||
|
dec Test.2;
|
||||||
|
let Test.3 : I64 = 1i64;
|
||||||
|
ret Test.3;
|
|
@ -0,0 +1,61 @@
|
||||||
|
# +emit:mono
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Op := {}
|
||||||
|
|
||||||
|
late = \a ->
|
||||||
|
dbg a
|
||||||
|
1
|
||||||
|
|
||||||
|
main =
|
||||||
|
late (@Op {})
|
||||||
|
|
||||||
|
# -emit:mono
|
||||||
|
procedure Inspect.251 (Inspect.252):
|
||||||
|
let Inspect.317 : Str = "<opaque>";
|
||||||
|
let Inspect.316 : Str = CallByName Inspect.61 Inspect.252 Inspect.317;
|
||||||
|
ret Inspect.316;
|
||||||
|
|
||||||
|
procedure Inspect.30 (Inspect.147):
|
||||||
|
ret Inspect.147;
|
||||||
|
|
||||||
|
procedure Inspect.35 (Inspect.300):
|
||||||
|
ret Inspect.300;
|
||||||
|
|
||||||
|
procedure Inspect.36 (Inspect.304):
|
||||||
|
let Inspect.311 : Str = "";
|
||||||
|
ret Inspect.311;
|
||||||
|
|
||||||
|
procedure Inspect.45 (Inspect.302):
|
||||||
|
let Inspect.314 : {} = Struct {};
|
||||||
|
let Inspect.313 : {} = CallByName Inspect.30 Inspect.314;
|
||||||
|
ret Inspect.313;
|
||||||
|
|
||||||
|
procedure Inspect.5 (Inspect.150):
|
||||||
|
let Inspect.312 : {} = CallByName Inspect.45 Inspect.150;
|
||||||
|
let Inspect.309 : {} = Struct {};
|
||||||
|
let Inspect.308 : Str = CallByName Inspect.36 Inspect.309;
|
||||||
|
let Inspect.307 : Str = CallByName Inspect.251 Inspect.308;
|
||||||
|
ret Inspect.307;
|
||||||
|
|
||||||
|
procedure Inspect.61 (Inspect.303, Inspect.298):
|
||||||
|
let Inspect.319 : Str = CallByName Str.3 Inspect.303 Inspect.298;
|
||||||
|
dec Inspect.298;
|
||||||
|
ret Inspect.319;
|
||||||
|
|
||||||
|
procedure Str.3 (#Attr.2, #Attr.3):
|
||||||
|
let Str.292 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||||
|
ret Str.292;
|
||||||
|
|
||||||
|
procedure Test.2 (Test.3):
|
||||||
|
let Test.8 : Str = CallByName Inspect.5 Test.3;
|
||||||
|
let Test.4 : Str = CallByName Inspect.35 Test.8;
|
||||||
|
dbg Test.4;
|
||||||
|
dec Test.4;
|
||||||
|
let Test.7 : I64 = 1i64;
|
||||||
|
ret Test.7;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.6 : {} = Struct {};
|
||||||
|
let Test.5 : I64 = CallByName Test.2 Test.6;
|
||||||
|
ret Test.5;
|
Loading…
Add table
Add a link
Reference in a new issue