From 4b132eefb48880a7a3781efe844d13845b070c07 Mon Sep 17 00:00:00 2001 From: kadmin Date: Sat, 22 Aug 2020 03:32:07 +0000 Subject: [PATCH 1/3] Initial commit --- compiler/rustc_typeck/src/astconv/generics.rs | 58 ++++++++++++++----- src/test/ui/const-generics/invalid-enum.rs | 17 ++++++ .../ui/const-generics/invalid-enum.stderr | 27 +++++++++ .../const-generics/issues/issue-62878.stderr | 2 + 4 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/const-generics/invalid-enum.rs create mode 100644 src/test/ui/const-generics/invalid-enum.stderr diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 84dab6de95819..43c7b30b78887 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -367,7 +367,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if position != GenericArgPosition::Type && !args.bindings.is_empty() { - Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); + AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); } let explicit_late_bound = @@ -392,7 +392,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if silent { - return Err(true); + return Err((0i32, None)); } // Unfortunately lifetime and type parameter mismatches are typically styled @@ -441,16 +441,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for span in spans { err.span_label(span, label.as_str()); } - err.emit(); - Err(true) + assert_ne!(bound, provided); + Err((bound as i32 - provided as i32, Some(err))) }; + let emit_correct = + |correct: Result<(), (_, Option>)>| match correct { + Ok(()) => Ok(()), + Err((v, None)) => Err(v == 0), + Err((v, Some(mut err))) => { + err.emit(); + Err(v == 0) + } + }; - let mut arg_count_correct = Ok(()); let mut unexpected_spans = vec![]; + let mut lifetime_count_correct = Ok(()); if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { - arg_count_correct = check_kind_count( + lifetime_count_correct = check_kind_count( "lifetime", param_counts.lifetimes, param_counts.lifetimes, @@ -458,12 +467,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { 0, &mut unexpected_spans, explicit_late_bound == ExplicitLateBound::Yes, - ) - .and(arg_count_correct); + ); } + // FIXME(const_generics:defaults) + let mut const_count_correct = Ok(()); if !infer_args || arg_counts.consts > param_counts.consts { - arg_count_correct = check_kind_count( + const_count_correct = check_kind_count( "const", param_counts.consts, param_counts.consts, @@ -471,13 +481,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.lifetimes + arg_counts.types, &mut unexpected_spans, false, - ) - .and(arg_count_correct); + ); } + // Note that type errors are currently be emitted *after* const errors. + let mut type_count_correct = Ok(()); if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize { - arg_count_correct = check_kind_count( + type_count_correct = check_kind_count( "type", param_counts.types - defaults.types - has_self as usize, param_counts.types - has_self as usize, @@ -485,10 +496,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.lifetimes, &mut unexpected_spans, false, - ) - .and(arg_count_correct); + ); } + // Emit a help message if it's possible that a type could be surrounded in braces + if let Err((c_mismatch, Some(ref mut _const_err))) = &mut const_count_correct { + if let Err((t_mismatch, Some(ref mut type_err))) = &mut type_count_correct { + if *c_mismatch == -*t_mismatch && *t_mismatch < 0 { + for i in 0..*c_mismatch as usize { + // let t_span = unexpected_type_spans[i].clone(); + let ident = args.args[arg_counts.lifetimes + i].id(); + type_err.help(&format!( + "For more complex types, surround with braces: `{{ {} }}`", + ident, + )); + } + } + } + } + + let arg_count_correct = emit_correct(lifetime_count_correct) + .and(emit_correct(const_count_correct)) + .and(emit_correct(type_count_correct)); + GenericArgCountResult { explicit_late_bound, correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { diff --git a/src/test/ui/const-generics/invalid-enum.rs b/src/test/ui/const-generics/invalid-enum.rs new file mode 100644 index 0000000000000..05c13e8fe0c88 --- /dev/null +++ b/src/test/ui/const-generics/invalid-enum.rs @@ -0,0 +1,17 @@ +#![feature(const_generics)] +#![allow(incomplete_features)] + +#[derive(PartialEq, Eq)] +enum CompileFlag { + A, + B, +} + +pub fn test() {} + +pub fn main() { + test::(); + //~^ ERROR: expected type, found variant + //~| ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments +} diff --git a/src/test/ui/const-generics/invalid-enum.stderr b/src/test/ui/const-generics/invalid-enum.stderr new file mode 100644 index 0000000000000..096e478d4abb0 --- /dev/null +++ b/src/test/ui/const-generics/invalid-enum.stderr @@ -0,0 +1,27 @@ +error[E0573]: expected type, found variant `CompileFlag::A` + --> $DIR/invalid-enum.rs:13:10 + | +LL | test::(); + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:13:3 + | +LL | test::(); + | ^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/invalid-enum.rs:13:10 + | +LL | test::(); + | ^^^^^^^^^^^^^^ unexpected type argument + | + = help: For more complex types, surround with braces: `{ HirId { owner: DefId(0:5 ~ invalid_enum[317d]::main[0]), local_id: 1 } }` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0573. +For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/issues/issue-62878.stderr b/src/test/ui/const-generics/issues/issue-62878.stderr index fe0990d8241fa..702fbfe84de62 100644 --- a/src/test/ui/const-generics/issues/issue-62878.stderr +++ b/src/test/ui/const-generics/issues/issue-62878.stderr @@ -24,6 +24,8 @@ error[E0107]: wrong number of type arguments: expected 0, found 1 | LL | foo::<_, {[1]}>(); | ^ unexpected type argument + | + = help: For more complex types, surround with braces: `{ HirId { owner: DefId(0:7 ~ issue_62878[317d]::main[0]), local_id: 1 } }` error[E0308]: mismatched types --> $DIR/issue-62878.rs:7:15 From 8d9187597a5e8ebcf17acba6b234884252b1a543 Mon Sep 17 00:00:00 2001 From: kadmin Date: Fri, 21 Aug 2020 06:55:14 +0000 Subject: [PATCH 2/3] Update to use multipart suggestion Nice. --- compiler/rustc_typeck/src/astconv/generics.rs | 43 ++++++++++++------- .../ui/const-generics/invalid-enum.stderr | 5 ++- .../const-generics/issues/issue-62878.stderr | 2 - 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 43c7b30b78887..b7336fcd36991 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -2,7 +2,7 @@ use crate::astconv::{ AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; use rustc_ast::ast::ParamKindOrd; -use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{GenericArg, GenericArgs}; @@ -448,10 +448,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let emit_correct = |correct: Result<(), (_, Option>)>| match correct { Ok(()) => Ok(()), - Err((v, None)) => Err(v == 0), - Err((v, Some(mut err))) => { + Err((_, None)) => Err(()), + Err((_, Some(mut err))) => { err.emit(); - Err(v == 0) + Err(()) } }; @@ -500,16 +500,27 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // Emit a help message if it's possible that a type could be surrounded in braces - if let Err((c_mismatch, Some(ref mut _const_err))) = &mut const_count_correct { - if let Err((t_mismatch, Some(ref mut type_err))) = &mut type_count_correct { - if *c_mismatch == -*t_mismatch && *t_mismatch < 0 { - for i in 0..*c_mismatch as usize { - // let t_span = unexpected_type_spans[i].clone(); - let ident = args.args[arg_counts.lifetimes + i].id(); - type_err.help(&format!( - "For more complex types, surround with braces: `{{ {} }}`", - ident, - )); + if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct { + if let Err((t_mismatch, Some(ref mut type_err))) = type_count_correct { + if c_mismatch == -t_mismatch && t_mismatch < 0 { + for i in 0..c_mismatch as usize { + let arg = &args.args[arg_counts.lifetimes + i]; + match arg { + GenericArg::Type(hir::Ty { + kind: hir::TyKind::Path { .. }, .. + }) => {} + _ => continue, + } + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + type_err.multipart_suggestion( + "If this generic argument was intended as a const parameter, \ + try surrounding it with braces:", + suggestions, + Applicability::MaybeIncorrect, + ); } } } @@ -521,8 +532,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { GenericArgCountResult { explicit_late_bound, - correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { - reported: if reported_err { Some(ErrorReported) } else { None }, + correct: arg_count_correct.map_err(|()| GenericArgCountMismatch { + reported: Some(ErrorReported), invalid_args: unexpected_spans, }), } diff --git a/src/test/ui/const-generics/invalid-enum.stderr b/src/test/ui/const-generics/invalid-enum.stderr index 096e478d4abb0..3291b62ea9fbb 100644 --- a/src/test/ui/const-generics/invalid-enum.stderr +++ b/src/test/ui/const-generics/invalid-enum.stderr @@ -19,7 +19,10 @@ error[E0107]: wrong number of type arguments: expected 0, found 1 LL | test::(); | ^^^^^^^^^^^^^^ unexpected type argument | - = help: For more complex types, surround with braces: `{ HirId { owner: DefId(0:5 ~ invalid_enum[317d]::main[0]), local_id: 1 } }` +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | test::<{ CompileFlag::A }>(); + | ^ ^ error: aborting due to 3 previous errors diff --git a/src/test/ui/const-generics/issues/issue-62878.stderr b/src/test/ui/const-generics/issues/issue-62878.stderr index 702fbfe84de62..fe0990d8241fa 100644 --- a/src/test/ui/const-generics/issues/issue-62878.stderr +++ b/src/test/ui/const-generics/issues/issue-62878.stderr @@ -24,8 +24,6 @@ error[E0107]: wrong number of type arguments: expected 0, found 1 | LL | foo::<_, {[1]}>(); | ^ unexpected type argument - | - = help: For more complex types, surround with braces: `{ HirId { owner: DefId(0:7 ~ issue_62878[317d]::main[0]), local_id: 1 } }` error[E0308]: mismatched types --> $DIR/issue-62878.rs:7:15 From 96bb2c86f2cfe03ad870d12b3b838e8b75eb1978 Mon Sep 17 00:00:00 2001 From: kadmin Date: Mon, 31 Aug 2020 17:12:53 +0000 Subject: [PATCH 3/3] Add further tests and liberalize type checking --- compiler/rustc_typeck/src/astconv/generics.rs | 61 ++++++------ src/test/ui/const-generics/invalid-enum.rs | 26 ++++- .../ui/const-generics/invalid-enum.stderr | 99 ++++++++++++++++--- 3 files changed, 139 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index b7336fcd36991..eb2207a0d675d 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -445,15 +445,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert_ne!(bound, provided); Err((bound as i32 - provided as i32, Some(err))) }; - let emit_correct = - |correct: Result<(), (_, Option>)>| match correct { - Ok(()) => Ok(()), - Err((_, None)) => Err(()), - Err((_, Some(mut err))) => { - err.emit(); - Err(()) - } - }; let mut unexpected_spans = vec![]; @@ -501,31 +492,41 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Emit a help message if it's possible that a type could be surrounded in braces if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct { - if let Err((t_mismatch, Some(ref mut type_err))) = type_count_correct { - if c_mismatch == -t_mismatch && t_mismatch < 0 { - for i in 0..c_mismatch as usize { - let arg = &args.args[arg_counts.lifetimes + i]; - match arg { - GenericArg::Type(hir::Ty { - kind: hir::TyKind::Path { .. }, .. - }) => {} - _ => continue, - } - let suggestions = vec![ - (arg.span().shrink_to_lo(), String::from("{ ")), - (arg.span().shrink_to_hi(), String::from(" }")), - ]; - type_err.multipart_suggestion( - "If this generic argument was intended as a const parameter, \ - try surrounding it with braces:", - suggestions, - Applicability::MaybeIncorrect, - ); - } + if let Err((_, Some(ref mut type_err))) = type_count_correct { + let possible_matches = args.args[arg_counts.lifetimes..] + .iter() + .filter(|arg| { + matches!( + arg, + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }) + ) + }) + .take(c_mismatch.max(0) as usize); + for arg in possible_matches { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + type_err.multipart_suggestion( + "If this generic argument was intended as a const parameter, \ + try surrounding it with braces:", + suggestions, + Applicability::MaybeIncorrect, + ); } } } + let emit_correct = + |correct: Result<(), (_, Option>)>| match correct { + Ok(()) => Ok(()), + Err((_, None)) => Err(()), + Err((_, Some(mut err))) => { + err.emit(); + Err(()) + } + }; + let arg_count_correct = emit_correct(lifetime_count_correct) .and(emit_correct(const_count_correct)) .and(emit_correct(type_count_correct)); diff --git a/src/test/ui/const-generics/invalid-enum.rs b/src/test/ui/const-generics/invalid-enum.rs index 05c13e8fe0c88..ceb188a0d3d34 100644 --- a/src/test/ui/const-generics/invalid-enum.rs +++ b/src/test/ui/const-generics/invalid-enum.rs @@ -7,11 +7,33 @@ enum CompileFlag { B, } -pub fn test() {} +pub fn test_1() {} +pub fn test_2(x: T) {} +pub struct Example{ + x: T, +} + +impl Example { + const ASSOC_FLAG: CompileFlag = CompileFlag::A; +} pub fn main() { - test::(); + test_1::(); + //~^ ERROR: expected type, found variant + //~| ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments + + test_2::<_, CompileFlag::A>(0); //~^ ERROR: expected type, found variant //~| ERROR: wrong number of const arguments //~| ERROR: wrong number of type arguments + + let _: Example = Example { x: 0 }; + //~^ ERROR: expected type, found variant + //~| ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments + + let _: Example = Example { x: 0 }; + //~^ ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments } diff --git a/src/test/ui/const-generics/invalid-enum.stderr b/src/test/ui/const-generics/invalid-enum.stderr index 3291b62ea9fbb..965abbc9cb7b8 100644 --- a/src/test/ui/const-generics/invalid-enum.stderr +++ b/src/test/ui/const-generics/invalid-enum.stderr @@ -1,30 +1,99 @@ error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:13:10 + --> $DIR/invalid-enum.rs:21:12 | -LL | test::(); - | ^^^^^^^^^^^^^^ - | | - | not a type - | help: try using the variant's enum: `CompileFlag` +LL | test_1::(); + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` + +error[E0573]: expected type, found variant `CompileFlag::A` + --> $DIR/invalid-enum.rs:26:15 + | +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` + +error[E0573]: expected type, found variant `CompileFlag::A` + --> $DIR/invalid-enum.rs:31:18 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:31:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected at most 1, found 2 + --> $DIR/invalid-enum.rs:31:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument + | +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; + | ^ ^ + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:36:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected at most 1, found 2 + --> $DIR/invalid-enum.rs:36:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument + | +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; + | ^ ^ error[E0107]: wrong number of const arguments: expected 1, found 0 - --> $DIR/invalid-enum.rs:13:3 + --> $DIR/invalid-enum.rs:21:3 | -LL | test::(); - | ^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument +LL | test_1::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument error[E0107]: wrong number of type arguments: expected 0, found 1 - --> $DIR/invalid-enum.rs:13:10 + --> $DIR/invalid-enum.rs:21:12 + | +LL | test_1::(); + | ^^^^^^^^^^^^^^ unexpected type argument + | +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | test_1::<{ CompileFlag::A }>(); + | ^ ^ + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:26:3 + | +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 1, found 2 + --> $DIR/invalid-enum.rs:26:15 | -LL | test::(); - | ^^^^^^^^^^^^^^ unexpected type argument +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^ unexpected type argument | help: If this generic argument was intended as a const parameter, try surrounding it with braces: | -LL | test::<{ CompileFlag::A }>(); - | ^ ^ +LL | test_2::<_, { CompileFlag::A }>(0); + | ^ ^ -error: aborting due to 3 previous errors +error: aborting due to 11 previous errors Some errors have detailed explanations: E0107, E0573. For more information about an error, try `rustc --explain E0107`.