-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[compiler v1] Better compiler error messages (#9222) #10505
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing type_msg_
to a more meaningful name and having it return bool
to indicate that the type could not be inferred and the caller should issue a warning would be more useful than creating a lot of warning messages that mostly won't be used.
type_msg_(context, ty, &format!("Cannot infer the type parameter {param_name} for generic function {m}::{n}. Try providing a type parameter.")); | ||
} | ||
|
||
pub fn type_msg_(context: &mut Context, ty: &mut Type, msg_uninferred: &str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name isn't not self-explanatory. Add a comment, perhaps something like:
/// Try to expand the type of ty, warning with msg_uninferred if type cannot be inferred.
HOWEVER, I think it would be better to change this method to try_to_infer_type(context, ty): bool
which returns true on success, and have the message be created only if the it returns failure. This would avoid creating a lot of unneeded warning messages, just in case they might be needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was halfway transitting, and realized this: there cannot be a try_to_infer_type(context, ty) -> bool
that lets the caller choose what to do when type inference fails. Consider when we are expanding S<X, Y>
, and we cannot infer X
. The caller only gets a false
, and doesn't know which parameter cannot be inferred, so no way to provide proper diagnostics.
Admittedly, type_msg_
is a bad name, but I derived that from the original type_
, which uses a general warning for uninferred types.
Hence I will maintain the current design, and add comments for type_msg_
.
pub fn type_(context: &mut Context, ty: &mut Type) { | ||
// type_ for expanding the `i`-th type param of struct `ty_name` | ||
fn type_struct_ty_param(context: &mut Context, ty: &mut Type, i: usize, ty_name: &TypeName_) { | ||
match ty_name { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using if let
destructuring here, which is nice to use when you have a match with just 2 branches, one of them being _
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
6d2f112
to
1195c84
Compare
Type_::Var(i) => { | ||
let last_tvar = forward_tvar(subst, i); | ||
match subst.get(last_tvar) { | ||
Some(sp!(_, Type_::Var(_))) => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is truly unreachable, why do you need to test for it? The Some(inner)
clause below encapsulates this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the decision made by the original author of unfold_type
. I think this is intended as a debug_assert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use panic!
with message as on line 114 in this code.
Otherwise its good practice to have this case explicit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is truly unreachable, why do you need to test for it? The
Some(inner)
clause below encapsulates this case.
It should be truly unreachable, and this is like a debug assert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
// Try to expand the type of ty, warning with msg_uninferred if type cannot be inferred. | ||
fn type_msg_(context: &mut Context, ty: &mut Type, msg_uninferred: &str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to use a lambda parameter for msg_uninferred so you can defer string construction unless it's needed. Probably need a dyn Fun to avoid generic expansion, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There doesn't seem to be any use case of type_msg_
where we know in advance that the expansion will always succeed. And since we are transitioning to compiler v2, I wonder if we need this extra power.
use Type_::*; | ||
match &mut ty.value { | ||
Anything | UnresolvedError | Param(_) | Unit => (), | ||
Ref(_, b) => type_(context, b), | ||
Var(tvar) => { | ||
let ty_tvar = sp(ty.loc, Var(*tvar)); | ||
let replacement = core::unfold_type(&context.subst, ty_tvar); | ||
fn unfold_type_mut(subst: &mut Subst, sp!(loc, t_): Type) -> Type { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not nest function definitions deeply inside other definitions, particular if its large like this. Pull to the outer scope. Also why is there mut
in the function name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Type_::Var(i) => { | ||
let last_tvar = forward_tvar(subst, i); | ||
match subst.get(last_tvar) { | ||
Some(sp!(_, Type_::Var(_))) => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use panic!
with message as on line 114 in this code.
Otherwise its good practice to have this case explicit
} | ||
|
||
// Try to expand the type of ty, warning with msg_uninferred if type cannot be inferred. | ||
fn type_msg_(context: &mut Context, ty: &mut Type, msg_uninferred: &str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better name then type_msg_
as type_with_context_msg
? Also the convention in this code to use a trailing _
in a name is when you try to avoid a keyword, or you are processing the non-spanned version of a type, which is not the case here, so no _
needed at the end of the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
use Type_::*; | ||
match &mut ty.value { | ||
Anything | UnresolvedError | Param(_) | Unit => (), | ||
Ref(_, b) => type_(context, b), | ||
Var(tvar) => { | ||
let ty_tvar = sp(ty.loc, Var(*tvar)); | ||
let replacement = core::unfold_type(&context.subst, ty_tvar); | ||
fn unfold_type_mut(subst: &mut Subst, sp!(loc, t_): Type) -> Type { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is core::unfold_type
not doing what you are trying to achieve here? It should unfold the type by its final replacement, does it not?
28ee3bb
to
e926c16
Compare
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #10505 +/- ##
===========================================
- Coverage 71.6% 68.9% -2.8%
===========================================
Files 2031 753 -1278
Lines 384787 172219 -212568
===========================================
- Hits 275757 118762 -156995
+ Misses 109030 53457 -55573 ☔ View full report in Codecov by Sentry. |
e926c16
to
ef93ebc
Compare
Type_::Var(i) => { | ||
let last_tvar = forward_tvar(subst, i); | ||
match subst.get(last_tvar) { | ||
Some(sp!(_, Type_::Var(_))) => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the decision made by the original author of unfold_type
. I think this is intended as a debug_assert.
} | ||
|
||
// Try to expand the type of ty, warning with msg_uninferred if type cannot be inferred. | ||
fn type_msg_(context: &mut Context, ty: &mut Type, msg_uninferred: &str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There doesn't seem to be any use case of type_msg_
where we know in advance that the expansion will always succeed. And since we are transitioning to compiler v2, I wonder if we need this extra power.
let replacement = match replacement { | ||
sp!(_, Var(_)) => panic!("ICE unfold_type_base failed to expand"), | ||
sp!(loc, Anything) => { | ||
let msg = "Could not infer this type. Try adding an annotation"; | ||
context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to avoid duplicate error messages for uninferred type variables. In the first time they are resolved to Err(_), and Anything for all following queries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment with this text.
pub fn type_(context: &mut Context, ty: &mut Type) { | ||
// type_ for expanding the `i`-th type param of struct `ty_name` | ||
fn type_struct_ty_param(context: &mut Context, ty: &mut Type, i: usize, ty_name: &TypeName_) { | ||
match ty_name { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Type_::Var(i) => { | ||
let last_tvar = forward_tvar(subst, i); | ||
match subst.get(last_tvar) { | ||
Some(sp!(_, Type_::Var(_))) => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is truly unreachable, why do you need to test for it? The
Some(inner)
clause below encapsulates this case.
It should be truly unreachable, and this is like a debug assert.
Type_::Var(i) => { | ||
let last_tvar = forward_tvar(subst, i); | ||
match subst.get(last_tvar) { | ||
Some(sp!(_, Type_::Var(_))) => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
// Try to expand the type of ty, warning with msg_uninferred if type cannot be inferred. | ||
fn type_msg_(context: &mut Context, ty: &mut Type, msg_uninferred: &str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
use Type_::*; | ||
match &mut ty.value { | ||
Anything | UnresolvedError | Param(_) | Unit => (), | ||
Ref(_, b) => type_(context, b), | ||
Var(tvar) => { | ||
let ty_tvar = sp(ty.loc, Var(*tvar)); | ||
let replacement = core::unfold_type(&context.subst, ty_tvar); | ||
fn unfold_type_mut(subst: &mut Subst, sp!(loc, t_): Type) -> Type { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
PTLA |
774b23e
to
950fa82
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see 2 suggested comments to add.
}, | ||
_ => panic!("ICE impossible. tapply switched to nontapply"), | ||
} | ||
}, | ||
} | ||
} | ||
|
||
pub fn type_(context: &mut Context, ty: &mut Type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While you're modifying this, can you add a comment to say what this function does? Perhaps
// Try to expand the type of ty, warning with a default message if type cannot be inferred.
This whole file is woefully undocumented. Thanks for figuring it out for us.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
let replacement = match replacement { | ||
sp!(_, Var(_)) => panic!("ICE unfold_type_base failed to expand"), | ||
sp!(loc, Anything) => { | ||
let msg = "Could not infer this type. Try adding an annotation"; | ||
context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment with this text.
950fa82
to
4b6645b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm.
For error messages on uninferred type vars, show the message only once, instead for each occurence of the same type var. For instance, if we have a generic struct `S<T>`. Compare the current ``` error[E04010]: cannot infer type ┌─ tests/move_check/typing/my_test0.move:5:9 │ 5 │ S{} = S{}; │ ^^^ Could not infer this type. Try adding an annotation error[E04010]: cannot infer type ┌─ tests/move_check/typing/my_test0.move:5:15 │ 5 │ S{} = S{}; │ ^^^ Could not infer this type. Try adding an annotation ``` with the new ``` ---- move_check_testsuite::typing/my_test0.move ---- Expected success. Unexpected diagnostics: error[E04010]: cannot infer type ┌─ tests/move_check/typing/my_test0.move:5:9 │ 5 │ S{} = S{}; │ ^^^ Could not infer this type. Try adding an annotation ```
4b6645b
to
5844979
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
✅ Forge suite
|
✅ Forge suite
|
✅ Forge suite
|
Improve the compiler error messages when type inference fails by - removing duplicated error messages for the same uninferred type variable (see `uninferred_type_unpack_assign.exp`). - telling why we cannot infer; e.g., which generic needs to be provided.
Improve the compiler error messages when type inference fails by - removing duplicated error messages for the same uninferred type variable (see `uninferred_type_unpack_assign.exp`). - telling why we cannot infer; e.g., which generic needs to be provided.
Improve the compiler error messages when type inference fails by - removing duplicated error messages for the same uninferred type variable (see `uninferred_type_unpack_assign.exp`). - telling why we cannot infer; e.g., which generic needs to be provided.
Description
This improves the compiler error messages when type inference fails by
Test Plan
Existing tests of compiler v1.