Report an error for lookups of unexposed values

This commit is contained in:
Richard Feldman 2020-12-10 23:17:35 -05:00
parent 7b09232911
commit 572c7cb3dd
3 changed files with 88 additions and 73 deletions

View File

@ -200,22 +200,20 @@ pub fn pre_constrain_imports(
match exposed_types.get(&module_id) { match exposed_types.get(&module_id) {
Some(ExposedModuleTypes::Valid(solved_types, new_aliases)) => { Some(ExposedModuleTypes::Valid(solved_types, new_aliases)) => {
let solved_type = solved_types.get(&symbol).unwrap_or_else(|| { // If the exposed value was invalid (e.g. it didn't have
panic!( // a corresponding definition), it won't have an entry
"Could not find {:?} in solved_types {:?}", // in solved_types
loc_symbol.value, solved_types if let Some(solved_type) = solved_types.get(&symbol) {
) // TODO should this be a union?
}); for (k, v) in new_aliases.clone() {
imported_aliases.insert(k, v);
}
// TODO should this be a union? imported_symbols.push(Import {
for (k, v) in new_aliases.clone() { loc_symbol,
imported_aliases.insert(k, v); solved_type: solved_type.clone(),
});
} }
imported_symbols.push(Import {
loc_symbol,
solved_type: solved_type.clone(),
});
} }
Some(ExposedModuleTypes::Invalid) => { Some(ExposedModuleTypes::Invalid) => {
// If that module was invalid, use True constraints // If that module was invalid, use True constraints

View File

@ -29,6 +29,22 @@ pub fn type_problem<'b>(
CircularType(region, symbol, overall_type) => { CircularType(region, symbol, overall_type) => {
to_circular_report(alloc, filename, region, symbol, overall_type) to_circular_report(alloc, filename, region, symbol, overall_type)
} }
UnexposedLookup(symbol) => {
let title = "UNRECOGNIZED NAME".to_string();
let doc = alloc
.stack(vec![alloc
.reflow("The ")
.append(alloc.module(symbol.module_id()))
.append(alloc.reflow(" module does not expose anything by the name "))
.append(alloc.symbol_unqualified(symbol))])
.append(alloc.reflow("."));
Report {
filename,
title,
doc,
}
}
BadType(type_problem) => { BadType(type_problem) => {
use roc_types::types::Problem::*; use roc_types::types::Problem::*;
match type_problem { match type_problem {

View File

@ -68,6 +68,7 @@ pub enum TypeError {
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>), BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),
CircularType(Region, Symbol, ErrorType), CircularType(Region, Symbol, ErrorType),
BadType(roc_types::types::Problem), BadType(roc_types::types::Problem),
UnexposedLookup(Symbol),
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
@ -273,69 +274,69 @@ fn solve(
} }
} }
Lookup(symbol, expectation, region) => { Lookup(symbol, expectation, region) => {
let var = *env.vars_by_symbol.get(&symbol).unwrap_or_else(|| { match env.vars_by_symbol.get(&symbol) {
// TODO Instead of panicking, solve this as True and record Some(var) => {
// a TypeError ("module Foo does not expose `bar`") for later. // Deep copy the vars associated with this symbol before unifying them.
panic!( // Otherwise, suppose we have this:
"Could not find symbol {:?} in vars_by_symbol {:?}", //
symbol, env.vars_by_symbol // identity = \a -> a
) //
}); // x = identity 5
//
// Deep copy the vars associated with this symbol before unifying them. // When we call (identity 5), it's important that we not unify
// Otherwise, suppose we have this: // on identity's original vars. If we do, the type of `identity` will be
// // mutated to be `Int -> Int` instead of `a -> `, which would be incorrect;
// identity = \a -> a // the type of `identity` is more general than that!
// //
// x = identity 5 // Instead, we want to unify on a *copy* of its vars. If the copy unifies
// // successfully (in this case, to `Int -> Int`), we can use that to
// When we call (identity 5), it's important that we not unify // infer the type of this lookup (in this case, `Int`) without ever
// on identity's original vars. If we do, the type of `identity` will be // having mutated the original.
// mutated to be `Int -> Int` instead of `a -> `, which would be incorrect; //
// the type of `identity` is more general than that! // If this Lookup is targeting a value in another module,
// // then we copy from that module's Subs into our own. If the value
// Instead, we want to unify on a *copy* of its vars. If the copy unifies // is being looked up in this module, then we use our Subs as both
// successfully (in this case, to `Int -> Int`), we can use that to // the source and destination.
// infer the type of this lookup (in this case, `Int`) without ever let actual = deep_copy_var(subs, rank, pools, *var);
// having mutated the original. let expected = type_to_var(
// subs,
// If this Lookup is targeting a value in another module, rank,
// then we copy from that module's Subs into our own. If the value pools,
// is being looked up in this module, then we use our Subs as both cached_aliases,
// the source and destination. expectation.get_type_ref(),
let actual = deep_copy_var(subs, rank, pools, var);
let expected = type_to_var(
subs,
rank,
pools,
cached_aliases,
expectation.get_type_ref(),
);
match unify(subs, actual, expected) {
Success(vars) => {
introduce(subs, rank, pools, &vars);
state
}
Failure(vars, actual_type, expected_type) => {
introduce(subs, rank, pools, &vars);
let problem = TypeError::BadExpr(
*region,
Category::Lookup(*symbol),
actual_type,
expectation.clone().replace(expected_type),
); );
match unify(subs, actual, expected) {
Success(vars) => {
introduce(subs, rank, pools, &vars);
problems.push(problem); state
}
state Failure(vars, actual_type, expected_type) => {
introduce(subs, rank, pools, &vars);
let problem = TypeError::BadExpr(
*region,
Category::Lookup(*symbol),
actual_type,
expectation.clone().replace(expected_type),
);
problems.push(problem);
state
}
BadType(vars, problem) => {
introduce(subs, rank, pools, &vars);
problems.push(TypeError::BadType(problem));
state
}
}
} }
BadType(vars, problem) => { None => {
introduce(subs, rank, pools, &vars); problems.push(TypeError::UnexposedLookup(*symbol));
problems.push(TypeError::BadType(problem));
state state
} }