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:
Ayaz 2023-12-01 00:42:11 -06:00 committed by GitHub
commit a56d7adc17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 285 additions and 90 deletions

View file

@ -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,

View file

@ -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!(),
},
}
}

View file

@ -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
);
}
} }

View file

@ -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;

View file

@ -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;