Auto merge of #71958 - Dylan-DPC:rollup-woxwt5d, r=Dylan-DPC

Rollup of 5 pull requests

Successful merges:

 - #70908 (Provide suggestions for type parameters missing bounds for associated types)
 - #71731 (Turn off rustc-dev-guide toolstate for now)
 - #71888 (refactor suggest_traits_to_import)
 - #71918 (Rename methods section)
 - #71950 (Miri validation error handling cleanup)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-05-06 20:38:56 +00:00
commit 29457dd92c
38 changed files with 951 additions and 486 deletions

View File

@ -88,7 +88,7 @@ static STABLE_TOOLS: &[(&str, &str)] = &[
static NIGHTLY_TOOLS: &[(&str, &str)] = &[
("miri", "src/tools/miri"),
("embedded-book", "src/doc/embedded-book"),
("rustc-dev-guide", "src/doc/rustc-dev-guide"),
// ("rustc-dev-guide", "src/doc/rustc-dev-guide"),
];
fn print_error(tool: &str, submodule: &str) {

View File

@ -14,7 +14,6 @@ python3 "$X_PY" test --no-fail-fast \
src/doc/rust-by-example \
src/doc/embedded-book \
src/doc/edition-guide \
src/doc/rustc-dev-guide \
src/tools/clippy \
src/tools/rls \
src/tools/rustfmt \

View File

@ -23,6 +23,7 @@ use rustc_session::Session;
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use std::mem;
use std::ops::DerefMut;
const MORE_EXTERN: &str =
"for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
@ -1113,17 +1114,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
for predicate in &generics.where_clause.predicates {
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
self.err_handler()
.struct_span_err(
predicate.span,
"equality constraints are not yet supported in `where` clauses",
)
.span_label(predicate.span, "not supported")
.note(
"see issue #20041 <https://github.com/rust-lang/rust/issues/20041> \
for more information",
)
.emit();
deny_equality_constraints(self, predicate, generics);
}
}
@ -1300,6 +1291,89 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
}
/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems
/// like it's setting an associated type, provide an appropriate suggestion.
fn deny_equality_constraints(
this: &mut AstValidator<'_>,
predicate: &WhereEqPredicate,
generics: &Generics,
) {
let mut err = this.err_handler().struct_span_err(
predicate.span,
"equality constraints are not yet supported in `where` clauses",
);
err.span_label(predicate.span, "not supported");
// Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind {
if let TyKind::Path(None, path) = &qself.ty.kind {
match &path.segments[..] {
[PathSegment { ident, args: None, .. }] => {
for param in &generics.params {
if param.ident == *ident {
let param = ident;
match &full_path.segments[qself.position..] {
[PathSegment { ident, .. }] => {
// Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
let mut assoc_path = full_path.clone();
// Remove `Bar` from `Foo::Bar`.
assoc_path.segments.pop();
let len = assoc_path.segments.len() - 1;
// Build `<Bar = RhsTy>`.
let arg = AngleBracketedArg::Constraint(AssocTyConstraint {
id: rustc_ast::node_id::DUMMY_NODE_ID,
ident: *ident,
kind: AssocTyConstraintKind::Equality {
ty: predicate.rhs_ty.clone(),
},
span: ident.span,
});
// Add `<Bar = RhsTy>` to `Foo`.
match &mut assoc_path.segments[len].args {
Some(args) => match args.deref_mut() {
GenericArgs::Parenthesized(_) => continue,
GenericArgs::AngleBracketed(args) => {
args.args.push(arg);
}
},
empty_args => {
*empty_args = AngleBracketedArgs {
span: ident.span,
args: vec![arg],
}
.into();
}
}
err.span_suggestion_verbose(
predicate.span,
&format!(
"if `{}` is an associated type you're trying to set, \
use the associated type binding syntax",
ident
),
format!(
"{}: {}",
param,
pprust::path_to_string(&assoc_path)
),
Applicability::MaybeIncorrect,
);
}
_ => {}
};
}
}
}
_ => {}
}
}
}
err.note(
"see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
);
err.emit();
}
pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
let mut validator = AstValidator {
session,

View File

@ -2626,8 +2626,42 @@ impl Node<'_> {
match self {
Node::TraitItem(TraitItem { generics, .. })
| Node::ImplItem(ImplItem { generics, .. })
| Node::Item(Item { kind: ItemKind::Fn(_, generics, _), .. }) => Some(generics),
| Node::Item(Item {
kind:
ItemKind::Trait(_, _, generics, ..)
| ItemKind::Impl { generics, .. }
| ItemKind::Fn(_, generics, _),
..
}) => Some(generics),
_ => None,
}
}
pub fn hir_id(&self) -> Option<HirId> {
match self {
Node::Item(Item { hir_id, .. })
| Node::ForeignItem(ForeignItem { hir_id, .. })
| Node::TraitItem(TraitItem { hir_id, .. })
| Node::ImplItem(ImplItem { hir_id, .. })
| Node::Field(StructField { hir_id, .. })
| Node::AnonConst(AnonConst { hir_id, .. })
| Node::Expr(Expr { hir_id, .. })
| Node::Stmt(Stmt { hir_id, .. })
| Node::Ty(Ty { hir_id, .. })
| Node::Binding(Pat { hir_id, .. })
| Node::Pat(Pat { hir_id, .. })
| Node::Arm(Arm { hir_id, .. })
| Node::Block(Block { hir_id, .. })
| Node::Local(Local { hir_id, .. })
| Node::MacroDef(MacroDef { hir_id, .. })
| Node::Lifetime(Lifetime { hir_id, .. })
| Node::Param(Param { hir_id, .. })
| Node::GenericParam(GenericParam { hir_id, .. }) => Some(*hir_id),
Node::TraitRef(TraitRef { hir_ref_id, .. }) => Some(*hir_ref_id),
Node::PathSegment(PathSegment { hir_id, .. }) => *hir_id,
Node::Variant(Variant { id, .. }) => Some(*id),
Node::Ctor(variant) => variant.ctor_hir_id(),
Node::Crate(_) | Node::Visibility(_) => None,
}
}
}

View File

@ -3,8 +3,7 @@ use super::{AllocId, Pointer, RawConst, ScalarMaybeUndef};
use crate::mir::interpret::ConstValue;
use crate::ty::layout::LayoutError;
use crate::ty::query::TyCtxtAt;
use crate::ty::tls;
use crate::ty::{self, layout, Ty};
use crate::ty::{self, layout, tls, FnSig, Ty};
use rustc_data_structures::sync::Lock;
use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported};
@ -329,7 +328,7 @@ impl fmt::Display for CheckInAllocMsg {
}
/// Error information for when the program caused Undefined Behavior.
pub enum UndefinedBehaviorInfo {
pub enum UndefinedBehaviorInfo<'tcx> {
/// Free-form case. Only for errors that are never caught!
Ub(String),
/// Unreachable code was executed.
@ -347,6 +346,8 @@ pub enum UndefinedBehaviorInfo {
PointerArithOverflow,
/// Invalid metadata in a wide pointer (using `str` to avoid allocations).
InvalidMeta(&'static str),
/// Invalid drop function in vtable.
InvalidDropFn(FnSig<'tcx>),
/// Reading a C string that does not end within its allocation.
UnterminatedCString(Pointer),
/// Dereferencing a dangling pointer after it got freed.
@ -380,6 +381,8 @@ pub enum UndefinedBehaviorInfo {
InvalidDiscriminant(ScalarMaybeUndef),
/// Using a pointer-not-to-a-function as function pointer.
InvalidFunctionPointer(Pointer),
/// Using a string that is not valid UTF-8,
InvalidStr(std::str::Utf8Error),
/// Using uninitialized data where it is not allowed.
InvalidUndefBytes(Option<Pointer>),
/// Working with a local that is not currently live.
@ -391,7 +394,7 @@ pub enum UndefinedBehaviorInfo {
},
}
impl fmt::Display for UndefinedBehaviorInfo {
impl fmt::Display for UndefinedBehaviorInfo<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use UndefinedBehaviorInfo::*;
match self {
@ -404,6 +407,11 @@ impl fmt::Display for UndefinedBehaviorInfo {
RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
InvalidDropFn(sig) => write!(
f,
"invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
sig
),
UnterminatedCString(p) => write!(
f,
"reading a null-terminated string starting at {} with no null found before end of allocation",
@ -446,6 +454,7 @@ impl fmt::Display for UndefinedBehaviorInfo {
InvalidFunctionPointer(p) => {
write!(f, "using {} as function pointer but it does not point to a function", p)
}
InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
InvalidUndefBytes(Some(p)) => write!(
f,
"reading uninitialized memory at {}, but this operation requires initialized memory",
@ -549,7 +558,7 @@ impl dyn MachineStopType {
pub enum InterpError<'tcx> {
/// The program caused undefined behavior.
UndefinedBehavior(UndefinedBehaviorInfo),
UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
/// The program did something the interpreter does not support (some of these *might* be UB
/// but the interpreter is not sure).
Unsupported(UnsupportedOpInfo),

View File

@ -2,7 +2,12 @@
use crate::ty::sty::InferTy;
use crate::ty::TyKind::*;
use crate::ty::TyS;
use crate::ty::{TyCtxt, TyS};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
use rustc_span::{BytePos, Span};
impl<'tcx> TyS<'tcx> {
/// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
@ -67,3 +72,180 @@ impl<'tcx> TyS<'tcx> {
}
}
}
/// Suggest restricting a type param with a new bound.
pub fn suggest_constraining_type_param(
tcx: TyCtxt<'_>,
generics: &hir::Generics<'_>,
err: &mut DiagnosticBuilder<'_>,
param_name: &str,
constraint: &str,
def_id: Option<DefId>,
) -> bool {
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
let param = if let Some(param) = param {
param
} else {
return false;
};
const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
let msg_restrict_type_further =
format!("consider further restricting type parameter `{}`", param_name);
if def_id == tcx.lang_items().sized_trait() {
// Type parameters are already `Sized` by default.
err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
return true;
}
let mut suggest_restrict = |span| {
err.span_suggestion_verbose(
span,
MSG_RESTRICT_BOUND_FURTHER,
format!(" + {}", constraint),
Applicability::MachineApplicable,
);
};
if param_name.starts_with("impl ") {
// If there's an `impl Trait` used in argument position, suggest
// restricting it:
//
// fn foo(t: impl Foo) { ... }
// --------
// |
// help: consider further restricting this bound with `+ Bar`
//
// Suggestion for tools in this case is:
//
// fn foo(t: impl Foo) { ... }
// --------
// |
// replace with: `impl Foo + Bar`
suggest_restrict(param.span.shrink_to_hi());
return true;
}
if generics.where_clause.predicates.is_empty()
// Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
{
if let Some(bounds_span) = param.bounds_span() {
// If user has provided some bounds, suggest restricting them:
//
// fn foo<T: Foo>(t: T) { ... }
// ---
// |
// help: consider further restricting this bound with `+ Bar`
//
// Suggestion for tools in this case is:
//
// fn foo<T: Foo>(t: T) { ... }
// --
// |
// replace with: `T: Bar +`
suggest_restrict(bounds_span.shrink_to_hi());
} else {
// If user hasn't provided any bounds, suggest adding a new one:
//
// fn foo<T>(t: T) { ... }
// - help: consider restricting this type parameter with `T: Foo`
err.span_suggestion_verbose(
param.span.shrink_to_hi(),
&msg_restrict_type,
format!(": {}", constraint),
Applicability::MachineApplicable,
);
}
true
} else {
// This part is a bit tricky, because using the `where` clause user can
// provide zero, one or many bounds for the same type parameter, so we
// have following cases to consider:
//
// 1) When the type parameter has been provided zero bounds
//
// Message:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
// - help: consider restricting this type parameter with `where X: Bar`
//
// Suggestion:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
// - insert: `, X: Bar`
//
//
// 2) When the type parameter has been provided one bound
//
// Message:
// fn foo<T>(t: T) where T: Foo { ... }
// ^^^^^^
// |
// help: consider further restricting this bound with `+ Bar`
//
// Suggestion:
// fn foo<T>(t: T) where T: Foo { ... }
// ^^
// |
// replace with: `T: Bar +`
//
//
// 3) When the type parameter has been provided many bounds
//
// Message:
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
// - help: consider further restricting this type parameter with `where T: Zar`
//
// Suggestion:
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
// - insert: `, T: Zar`
let mut param_spans = Vec::new();
for predicate in generics.where_clause.predicates {
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
span, bounded_ty, ..
}) = predicate
{
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
if let Some(segment) = path.segments.first() {
if segment.ident.to_string() == param_name {
param_spans.push(span);
}
}
}
}
}
let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place();
// Account for `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
let mut trailing_comma = false;
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) {
trailing_comma = snippet.ends_with(',');
}
let where_clause_span = if trailing_comma {
let hi = where_clause_span.hi();
Span::new(hi - BytePos(1), hi, where_clause_span.ctxt())
} else {
where_clause_span.shrink_to_hi()
};
match &param_spans[..] {
&[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
_ => {
err.span_suggestion_verbose(
where_clause_span,
&msg_restrict_type_further,
format!(", {}: {}", param_name, constraint),
Applicability::MachineApplicable,
);
}
}
true
}
}

View File

@ -1,4 +1,5 @@
use crate::traits::{ObligationCause, ObligationCauseCode};
use crate::ty::diagnostics::suggest_constraining_type_param;
use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt};
use rustc_ast::ast;
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
@ -401,8 +402,46 @@ impl<'tcx> TyCtxt<'tcx> {
(ty::Projection(_), ty::Projection(_)) => {
db.note("an associated type was expected, but a different one was found");
}
(ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => {
db.note("you might be missing a type parameter or trait bound");
(ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => {
let generics = self.generics_of(body_owner_def_id);
let p_span = self.def_span(generics.type_param(p, self).def_id);
if !sp.contains(p_span) {
db.span_label(p_span, "this type parameter");
}
let hir = self.hir();
let mut note = true;
if let Some(generics) = generics
.type_param(p, self)
.def_id
.as_local()
.map(|id| hir.as_local_hir_id(id))
.and_then(|id| self.hir().find(self.hir().get_parent_node(id)))
.as_ref()
.and_then(|node| node.generics())
{
// Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics.
let trait_ref = proj.trait_ref(self);
let path =
self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
let item_name = self.item_name(proj.item_def_id);
let path = if path.ends_with('>') {
format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p)
} else {
format!("{}<{} = {}>", path, item_name, p)
};
note = !suggest_constraining_type_param(
self,
generics,
db,
&format!("{}", proj.self_ty()),
&path,
None,
);
}
if note {
db.note("you might be missing a type parameter or trait bound");
}
}
(ty::Param(p), ty::Dynamic(..) | ty::Opaque(..))
| (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => {

View File

@ -10,10 +10,9 @@ use rustc_middle::mir::{
FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
use rustc_span::source_map::DesugaringKind;
use rustc_span::Span;
use rustc_trait_selection::traits::error_reporting::suggest_constraining_type_param;
use crate::dataflow::drop_flag_effects;
use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex};

View File

@ -327,8 +327,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> {
let len = mplace.len(self)?;
let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?;
let str = ::std::str::from_utf8(bytes)
.map_err(|err| err_ub_format!("this string is not valid UTF-8: {}", err))?;
let str = ::std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
Ok(str)
}

View File

@ -147,14 +147,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// The drop function takes `*mut T` where `T` is the type being dropped, so get that.
let args = fn_sig.inputs();
if args.len() != 1 {
throw_ub_format!("drop fn should have 1 argument, but signature is {:?}", fn_sig);
throw_ub!(InvalidDropFn(fn_sig));
}
let ty = args[0]
.builtin_deref(true)
.ok_or_else(|| {
err_ub_format!("drop fn argument type {} is not a pointer type", args[0])
})?
.ty;
let ty = args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidDropFn(fn_sig)))?.ty;
Ok((drop_instance, ty))
}

View File

@ -25,43 +25,39 @@ use super::{
};
macro_rules! throw_validation_failure {
($what:expr, $where:expr $(, $expected:expr )?) => {{
let mut msg = format!("encountered {}", $what);
($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{
let mut msg = String::new();
msg.push_str("encountered ");
write!(&mut msg, $($what_fmt),+).unwrap();
let where_ = &$where;
if !where_.is_empty() {
msg.push_str(" at ");
write_path(&mut msg, where_);
}
$( write!(&mut msg, ", but expected {}", $expected).unwrap(); )?
$(
msg.push_str(", but expected ");
write!(&mut msg, $($expected_fmt),+).unwrap();
)?
throw_ub!(ValidationFailure(msg))
}};
}
/// Returns a validation failure for any Err value of $e.
// FIXME: Replace all usages of try_validation! with try_validation_pat!.
macro_rules! try_validation {
($e:expr, $what:expr, $where:expr $(, $expected:expr )?) => {{
try_validation_pat!($e, $where, {
_ => { "{}", $what } $( expected { "{}", $expected } )?,
})
}};
}
/// Like try_validation, but will throw a validation error if any of the patterns in $p are
/// matched. Other errors are passed back to the caller, unchanged. This lets you use the patterns
/// as a kind of validation blacklist:
/// If $e throws an error matching the pattern, throw a validation failure.
/// Other errors are passed back to the caller, unchanged -- and if they reach the root of
/// the visitor, we make sure only validation errors and `InvalidProgram` errors are left.
/// This lets you use the patterns as a kind of validation whitelist, asserting which errors
/// can possibly happen:
///
/// ```
/// let v = try_validation_pat!(some_fn(), some_path, {
/// let v = try_validation!(some_fn(), some_path, {
/// Foo | Bar | Baz => { "some failure" },
/// });
/// // Failures that match $p are thrown up as validation errors, but other errors are passed back
/// // unchanged.
/// ```
///
/// An additional expected parameter can also be added to the failure message:
///
/// ```
/// let v = try_validation_pat!(some_fn(), some_path, {
/// let v = try_validation!(some_fn(), some_path, {
/// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" },
/// });
/// ```
@ -70,24 +66,25 @@ macro_rules! try_validation {
/// the format string in directly:
///
/// ```
/// let v = try_validation_pat!(some_fn(), some_path, {
/// let v = try_validation!(some_fn(), some_path, {
/// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value },
/// });
/// ```
///
macro_rules! try_validation_pat {
($e:expr, $where:expr, { $( $p:pat )|+ =>
{ $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? $( , )?}) => {{
macro_rules! try_validation {
($e:expr, $where:expr,
$( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)?
) => {{
match $e {
Ok(x) => x,
// We catch the error and turn it into a validation failure. We are okay with
// allocation here as this can only slow down builds that fail anyway.
$( Err(InterpErrorInfo { kind: $p, .. }) )|+ =>
$( $( Err(InterpErrorInfo { kind: $p, .. }) )|+ =>
throw_validation_failure!(
format_args!($( $what_fmt ),+),
$where
$(, format_args!($( $expected_fmt ),+))?
$where,
{ $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )?
),
)+
#[allow(unreachable_patterns)]
Err(e) => Err::<!, _>(e)?,
}
@ -303,32 +300,45 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
match tail.kind {
ty::Dynamic(..) => {
let vtable = meta.unwrap_meta();
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
try_validation!(
self.ecx.memory.check_ptr_access(
self.ecx.memory.check_ptr_access_align(
vtable,
3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
self.ecx.tcx.data_layout.pointer_align.abi,
Some(self.ecx.tcx.data_layout.pointer_align.abi),
CheckInAllocMsg::InboundsTest,
),
"dangling or unaligned vtable pointer in wide pointer or too small vtable",
self.path
self.path,
err_ub!(DanglingIntPointer(..)) |
err_ub!(PointerUseAfterFree(..)) |
err_unsup!(ReadBytesAsPointer) =>
{ "dangling vtable pointer in wide pointer" },
err_ub!(AlignmentCheckFailed { .. }) =>
{ "unaligned vtable pointer in wide pointer" },
err_ub!(PointerOutOfBounds { .. }) =>
{ "too small vtable" },
);
try_validation!(
self.ecx.read_drop_type_from_vtable(vtable),
"invalid drop fn in vtable",
self.path
self.path,
err_ub!(InvalidDropFn(..)) |
err_ub!(DanglingIntPointer(..)) |
err_ub!(InvalidFunctionPointer(..)) |
err_unsup!(ReadBytesAsPointer) =>
{ "invalid drop function pointer in vtable" },
);
try_validation!(
self.ecx.read_size_and_align_from_vtable(vtable),
"invalid size or align in vtable",
self.path
self.path,
err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" },
);
// FIXME: More checks for the vtable.
}
ty::Slice(..) | ty::Str => {
let _len = try_validation!(
meta.unwrap_meta().to_machine_usize(self.ecx),
"non-integer slice length in wide pointer",
self.path
self.path,
err_unsup!(ReadPointerAsBytes) => { "non-integer slice length in wide pointer" },
);
// We do not check that `len * elem_size <= isize::MAX`:
// that is only required for references, and there it falls out of the
@ -354,78 +364,52 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// Check metadata early, for better diagnostics
let place = try_validation!(
self.ecx.ref_to_mplace(value),
format_args!("uninitialized {}", kind),
self.path
self.path,
err_ub!(InvalidUndefBytes(..)) => { "uninitialized {}", kind },
);
if place.layout.is_unsized() {
self.check_wide_ptr_meta(place.meta, place.layout)?;
}
// Make sure this is dereferenceable and all.
let size_and_align = match self.ecx.size_and_align_of(place.meta, place.layout) {
Ok(res) => res,
Err(err) => match err.kind {
err_ub!(InvalidMeta(msg)) => throw_validation_failure!(
format_args!("invalid {} metadata: {}", kind, msg),
self.path
),
_ => bug!("unexpected error during ptr size_and_align_of: {}", err),
},
};
let size_and_align = try_validation!(
self.ecx.size_and_align_of(place.meta, place.layout),
self.path,
err_ub!(InvalidMeta(msg)) => { "invalid {} metadata: {}", kind, msg },
);
let (size, align) = size_and_align
// for the purpose of validity, consider foreign types to have
// alignment and size determined by the layout (size will be 0,
// alignment should take attributes into account).
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align(
place.ptr,
size,
Some(align),
CheckInAllocMsg::InboundsTest,
) {
Ok(ptr) => ptr,
Err(err) => {
info!(
"{:?} did not pass access check for size {:?}, align {:?}",
place.ptr, size, align
);
match err.kind {
err_ub!(DanglingIntPointer(0, _)) => {
throw_validation_failure!(format_args!("a NULL {}", kind), self.path)
}
err_ub!(DanglingIntPointer(i, _)) => throw_validation_failure!(
format_args!("a {} to unallocated address {}", kind, i),
self.path
),
err_ub!(AlignmentCheckFailed { required, has }) => throw_validation_failure!(
format_args!(
"an unaligned {} (required {} byte alignment but found {})",
kind,
required.bytes(),
has.bytes()
),
self.path
),
err_unsup!(ReadBytesAsPointer) => throw_validation_failure!(
format_args!("a dangling {} (created from integer)", kind),
self.path
),
err_ub!(PointerOutOfBounds { .. }) => throw_validation_failure!(
format_args!(
"a dangling {} (going beyond the bounds of its allocation)",
kind
),
self.path
),
// This cannot happen during const-eval (because interning already detects
// dangling pointers), but it can happen in Miri.
err_ub!(PointerUseAfterFree(_)) => throw_validation_failure!(
format_args!("a dangling {} (use-after-free)", kind),
self.path
),
_ => bug!("Unexpected error during ptr inbounds test: {}", err),
}
}
};
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
let ptr: Option<_> = try_validation!(
self.ecx.memory.check_ptr_access_align(
place.ptr,
size,
Some(align),
CheckInAllocMsg::InboundsTest,
),
self.path,
err_ub!(AlignmentCheckFailed { required, has }) =>
{
"an unaligned {} (required {} byte alignment but found {})",
kind,
required.bytes(),
has.bytes()
},
err_ub!(DanglingIntPointer(0, _)) =>
{ "a NULL {}", kind },
err_ub!(DanglingIntPointer(i, _)) =>
{ "a dangling {} (address {} is unallocated)", kind, i },
err_ub!(PointerOutOfBounds { .. }) =>
{ "a dangling {} (going beyond the bounds of its allocation)", kind },
err_unsup!(ReadBytesAsPointer) =>
{ "a dangling {} (created from integer)", kind },
// This cannot happen during const-eval (because interning already detects
// dangling pointers), but it can happen in Miri.
err_ub!(PointerUseAfterFree(..)) =>
{ "a dangling {} (use-after-free)", kind },
);
// Recursive checking
if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts {
if let Some(ptr) = ptr {
@ -440,9 +424,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// We also need to do it here instead of going on to avoid running
// into the `before_access_global` check during validation.
if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
throw_validation_failure!(
format_args!("a {} pointing to a static variable", kind),
self.path
throw_validation_failure!(self.path,
{ "a {} pointing to a static variable", kind }
);
}
// `extern static` cannot be validated as they have no body.
@ -489,12 +472,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
match ty.kind {
ty::Bool => {
let value = self.ecx.read_scalar(value)?;
try_validation!(value.to_bool(), value, self.path, "a boolean");
try_validation!(
value.to_bool(),
self.path,
err_ub!(InvalidBool(..)) => { "{}", value } expected { "a boolean" },
);
Ok(true)
}
ty::Char => {
let value = self.ecx.read_scalar(value)?;
try_validation!(value.to_char(), value, self.path, "a valid unicode codepoint");
try_validation!(
value.to_char(),
self.path,
err_ub!(InvalidChar(..)) => { "{}", value } expected { "a valid unicode codepoint" },
);
Ok(true)
}
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
@ -505,10 +496,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
let is_bits = value.not_undef().map_or(false, |v| v.is_bits());
if !is_bits {
throw_validation_failure!(
value,
self.path,
"initialized plain (non-pointer) bytes"
throw_validation_failure!(self.path,
{ "{}", value } expected { "initialized plain (non-pointer) bytes" }
)
}
} else {
@ -521,9 +510,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// We are conservative with undef for integers, but try to
// actually enforce the strict rules for raw pointers (mostly because
// that lets us re-use `ref_to_mplace`).
let place = try_validation_pat!(self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), self.path, {
let place = try_validation!(
self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?),
self.path,
err_ub!(InvalidUndefBytes(..)) => { "uninitialized raw pointer" },
});
);
if place.layout.is_unsized() {
self.check_wide_ptr_meta(place.meta, place.layout)?;
}
@ -541,14 +532,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let value = self.ecx.read_scalar(value)?;
let _fn = try_validation!(
value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
value,
self.path,
"a function pointer"
err_ub!(DanglingIntPointer(..)) |
err_ub!(InvalidFunctionPointer(..)) |
err_unsup!(ReadBytesAsPointer) =>
{ "{}", value } expected { "a function pointer" },
);
// FIXME: Check if the signature matches
Ok(true)
}
ty::Never => throw_validation_failure!("a value of the never type `!`", self.path),
ty::Never => throw_validation_failure!(self.path, { "a value of the never type `!`" }),
ty::Foreign(..) | ty::FnDef(..) => {
// Nothing to check.
Ok(true)
@ -598,35 +591,33 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// At least one value is excluded. Get the bits.
let value = try_validation!(
value.not_undef(),
value,
self.path,
format_args!("something {}", wrapping_range_format(valid_range, max_hi),)
err_ub!(InvalidUndefBytes(..)) => { "{}", value }
expected { "something {}", wrapping_range_format(valid_range, max_hi) },
);
let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
Err(ptr) => {
if lo == 1 && hi == max_hi {
// Only NULL is the niche. So make sure the ptr is NOT NULL.
if self.ecx.memory.ptr_may_be_null(ptr) {
throw_validation_failure!(
"a potentially NULL pointer",
self.path,
format_args!(
throw_validation_failure!(self.path,
{ "a potentially NULL pointer" }
expected {
"something that cannot possibly fail to be {}",
wrapping_range_format(valid_range, max_hi)
)
}
)
}
return Ok(());
} else {
// Conservatively, we reject, because the pointer *could* have a bad
// value.
throw_validation_failure!(
"a pointer",
self.path,
format_args!(
throw_validation_failure!(self.path,
{ "a pointer" }
expected {
"something that cannot possibly fail to be {}",
wrapping_range_format(valid_range, max_hi)
)
}
)
}
}
@ -636,10 +627,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
if wrapping_range_contains(&valid_range, bits) {
Ok(())
} else {
throw_validation_failure!(
bits,
self.path,
format_args!("something {}", wrapping_range_format(valid_range, max_hi))
throw_validation_failure!(self.path,
{ "{}", bits }
expected { "something {}", wrapping_range_format(valid_range, max_hi) }
)
}
}
@ -703,19 +693,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
assert!(op.layout.ty.builtin_deref(true).is_none());
// Recursively walk the type. Translate some possible errors to something nicer.
match self.walk_value(op) {
Ok(()) => {}
Err(err) => match err.kind {
err_ub!(InvalidDiscriminant(val)) => {
throw_validation_failure!(val, self.path, "a valid enum discriminant")
}
err_unsup!(ReadPointerAsBytes) => {
throw_validation_failure!("a pointer", self.path, "plain (non-pointer) bytes")
}
// Propagate upwards (that will also check for unexpected errors).
_ => return Err(err),
},
}
try_validation!(
self.walk_value(op),
self.path,
err_ub!(InvalidDiscriminant(val)) =>
{ "{}", val } expected { "a valid enum discriminant" },
err_unsup!(ReadPointerAsBytes) =>
{ "a pointer" } expected { "plain (non-pointer) bytes" },
);
// *After* all of this, check the ABI. We need to check the ABI to handle
// types like `NonNull` where the `Scalar` info is more restrictive than what
@ -729,9 +714,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// MyNewtype and then the scalar in there).
match op.layout.abi {
Abi::Uninhabited => {
throw_validation_failure!(
format_args!("a value of uninhabited type {:?}", op.layout.ty),
self.path
throw_validation_failure!(self.path,
{ "a value of uninhabited type {:?}", op.layout.ty }
);
}
Abi::Scalar(ref scalar_layout) => {
@ -761,8 +745,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
let mplace = op.assert_mem_place(self.ecx); // strings are never immediate
try_validation!(
self.ecx.read_str(mplace),
"uninitialized or non-UTF-8 data in str",
self.path
self.path,
err_ub!(InvalidStr(..)) => { "uninitialized or non-UTF-8 data in str" },
);
}
ty::Array(tys, ..) | ty::Slice(tys)
@ -815,7 +799,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
Ok(()) => {}
// Some error happened, try to provide a more detailed description.
Err(err) => {
// For some errors we might be able to provide extra information
// For some errors we might be able to provide extra information.
// (This custom logic does not fit the `try_validation!` macro.)
match err.kind {
err_ub!(InvalidUndefBytes(Some(ptr))) => {
// Some byte was uninitialized, determine which
@ -825,7 +810,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
.unwrap();
self.path.push(PathElem::ArrayElem(i));
throw_validation_failure!("uninitialized bytes", self.path)
throw_validation_failure!(self.path, { "uninitialized bytes" })
}
// Propagate upwards (that will also check for unexpected errors).
_ => return Err(err),
@ -876,7 +861,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// validate and each caller will know best what to do with them.
Err(err) if matches!(err.kind, InterpError::InvalidProgram(_)) => Err(err),
// Avoid other errors as those do not show *where* in the value the issue lies.
Err(err) => bug!("Unexpected error during validation: {}", err),
Err(err) => {
err.print_backtrace();
bug!("Unexpected error during validation: {}", err);
}
}
}

View File

@ -15,17 +15,16 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::{Node, QPath, TyKind, WhereBoundPredicate, WherePredicate};
use rustc_hir::Node;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::fast_reject;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::SubtypePredicate;
use rustc_middle::ty::{
self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt,
TypeFoldable, WithConstness,
};
use rustc_session::DiagnosticMessageId;
use rustc_span::{BytePos, ExpnKind, Span, DUMMY_SP};
use rustc_span::{ExpnKind, Span, DUMMY_SP};
use std::fmt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
@ -1700,180 +1699,3 @@ impl ArgKind {
}
}
}
/// Suggest restricting a type param with a new bound.
pub fn suggest_constraining_type_param(
tcx: TyCtxt<'_>,
generics: &hir::Generics<'_>,
err: &mut DiagnosticBuilder<'_>,
param_name: &str,
constraint: &str,
def_id: Option<DefId>,
) -> bool {
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
let param = if let Some(param) = param {
param
} else {
return false;
};
const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound";
let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name);
let msg_restrict_type_further =
format!("consider further restricting type parameter `{}`", param_name);
if def_id == tcx.lang_items().sized_trait() {
// Type parameters are already `Sized` by default.
err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint));
return true;
}
let mut suggest_restrict = |span| {
err.span_suggestion_verbose(
span,
MSG_RESTRICT_BOUND_FURTHER,
format!(" + {}", constraint),
Applicability::MachineApplicable,
);
};
if param_name.starts_with("impl ") {
// If there's an `impl Trait` used in argument position, suggest
// restricting it:
//
// fn foo(t: impl Foo) { ... }
// --------
// |
// help: consider further restricting this bound with `+ Bar`
//
// Suggestion for tools in this case is:
//
// fn foo(t: impl Foo) { ... }
// --------
// |
// replace with: `impl Foo + Bar`
suggest_restrict(param.span.shrink_to_hi());
return true;
}
if generics.where_clause.predicates.is_empty()
// Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
{
if let Some(bounds_span) = param.bounds_span() {
// If user has provided some bounds, suggest restricting them:
//
// fn foo<T: Foo>(t: T) { ... }
// ---
// |
// help: consider further restricting this bound with `+ Bar`
//
// Suggestion for tools in this case is:
//
// fn foo<T: Foo>(t: T) { ... }
// --
// |
// replace with: `T: Bar +`
suggest_restrict(bounds_span.shrink_to_hi());
} else {
// If user hasn't provided any bounds, suggest adding a new one:
//
// fn foo<T>(t: T) { ... }
// - help: consider restricting this type parameter with `T: Foo`
err.span_suggestion_verbose(
param.span.shrink_to_hi(),
&msg_restrict_type,
format!(": {}", constraint),
Applicability::MachineApplicable,
);
}
true
} else {
// This part is a bit tricky, because using the `where` clause user can
// provide zero, one or many bounds for the same type parameter, so we
// have following cases to consider:
//
// 1) When the type parameter has been provided zero bounds
//
// Message:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
// - help: consider restricting this type parameter with `where X: Bar`
//
// Suggestion:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
// - insert: `, X: Bar`
//
//
// 2) When the type parameter has been provided one bound
//
// Message:
// fn foo<T>(t: T) where T: Foo { ... }
// ^^^^^^
// |
// help: consider further restricting this bound with `+ Bar`
//
// Suggestion:
// fn foo<T>(t: T) where T: Foo { ... }
// ^^
// |
// replace with: `T: Bar +`
//
//
// 3) When the type parameter has been provided many bounds
//
// Message:
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
// - help: consider further restricting this type parameter with `where T: Zar`
//
// Suggestion:
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
// - insert: `, T: Zar`
let mut param_spans = Vec::new();
for predicate in generics.where_clause.predicates {
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
span, bounded_ty, ..
}) = predicate
{
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
if let Some(segment) = path.segments.first() {
if segment.ident.to_string() == param_name {
param_spans.push(span);
}
}
}
}
}
let where_clause_span = generics.where_clause.span_for_predicates_or_empty_place();
// Account for `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
let mut trailing_comma = false;
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(where_clause_span) {
trailing_comma = snippet.ends_with(',');
}
let where_clause_span = if trailing_comma {
let hi = where_clause_span.hi();
Span::new(hi - BytePos(1), hi, where_clause_span.ctxt())
} else {
where_clause_span.shrink_to_hi()
};
match &param_spans[..] {
&[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
_ => {
err.span_suggestion_verbose(
where_clause_span,
&msg_restrict_type_further,
format!(", {}: {}", param_name, constraint),
Applicability::MachineApplicable,
);
}
}
true
}
}

View File

@ -3,7 +3,6 @@ use super::{
};
use crate::infer::InferCtxt;
use crate::traits::error_reporting::suggest_constraining_type_param;
use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style};
use rustc_hir as hir;
@ -13,7 +12,8 @@ use rustc_hir::intravisit::Visitor;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
use rustc_middle::ty::TypeckTables;
use rustc_middle::ty::{
self, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty,
TyCtxt, TypeFoldable, WithConstness,
};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{MultiSpan, Span, DUMMY_SP};

View File

@ -947,65 +947,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// this isn't perfect (that is, there are cases when
// implementing a trait would be legal but is rejected
// here).
!unsatisfied_predicates.iter().any(|(p, _)| match p {
// Hide traits if they are present in predicates as they can be fixed without
// having to implement them.
ty::Predicate::Trait(t, _) => t.def_id() != info.def_id,
ty::Predicate::Projection(p) => p.item_def_id() != info.def_id,
_ => true,
}) && (type_is_local || info.def_id.is_local())
&& self
.associated_item(info.def_id, item_name, Namespace::ValueNS)
.filter(|item| {
if let ty::AssocKind::Fn = item.kind {
let id = item.def_id.as_local().map(|def_id| {
self.tcx.hir().as_local_hir_id(def_id)
});
if let Some(hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Fn(fn_sig, method),
..
})) = id.map(|id| self.tcx.hir().get(id))
{
let self_first_arg = match method {
hir::TraitFn::Required([ident, ..]) => {
ident.name == kw::SelfLower
}
hir::TraitFn::Provided(body_id) => {
match &self.tcx.hir().body(*body_id).params[..] {
[hir::Param {
pat:
hir::Pat {
kind:
hir::PatKind::Binding(
_,
_,
ident,
..,
),
..
},
..
}, ..] => ident.name == kw::SelfLower,
_ => false,
}
}
_ => false,
};
if !fn_sig.decl.implicit_self.has_implicit_self()
&& self_first_arg
{
if let Some(ty) = fn_sig.decl.inputs.get(0) {
arbitrary_rcvr.push(ty.span);
}
return false;
unsatisfied_predicates.iter().all(|(p, _)| match p {
// Hide traits if they are present in predicates as they can be fixed without
// having to implement them.
ty::Predicate::Trait(t, _) => t.def_id() == info.def_id,
ty::Predicate::Projection(p) => p.item_def_id() == info.def_id,
_ => false,
}) && (type_is_local || info.def_id.is_local())
&& self
.associated_item(info.def_id, item_name, Namespace::ValueNS)
.filter(|item| {
if let ty::AssocKind::Fn = item.kind {
let id = item
.def_id
.as_local()
.map(|def_id| self.tcx.hir().as_local_hir_id(def_id));
if let Some(hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Fn(fn_sig, method),
..
})) = id.map(|id| self.tcx.hir().get(id))
{
let self_first_arg = match method {
hir::TraitFn::Required([ident, ..]) => {
ident.name == kw::SelfLower
}
hir::TraitFn::Provided(body_id) => {
self.tcx.hir().body(*body_id).params.first().map_or(
false,
|param| {
matches!(
param.pat.kind,
hir::PatKind::Binding(_, _, ident, _)
if ident.name == kw::SelfLower
)
},
)
}
_ => false,
};
if !fn_sig.decl.implicit_self.has_implicit_self()
&& self_first_arg
{
if let Some(ty) = fn_sig.decl.inputs.get(0) {
arbitrary_rcvr.push(ty.span);
}
return false;
}
}
// We only want to suggest public or local traits (#45781).
item.vis == ty::Visibility::Public || info.def_id.is_local()
})
.is_some()
}
// We only want to suggest public or local traits (#45781).
item.vis == ty::Visibility::Public || info.def_id.is_local()
})
.is_some()
})
.collect::<Vec<_>>();
for span in &arbitrary_rcvr {

View File

@ -10,7 +10,7 @@ use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
};
use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
@ -253,6 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// error types are considered "builtin"
if !lhs_ty.references_error() && !rhs_ty.references_error() {
let source_map = self.tcx.sess.source_map();
match is_assign {
IsAssign::Yes => {
let mut err = struct_span_err!(
@ -317,12 +318,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This has nothing here because it means we did string
// concatenation (e.g., "Hello " += "World!"). This means
// we don't want the note in the else clause to be emitted
} else if let ty::Param(_) = lhs_ty.kind {
// FIXME: point to span of param
err.note(&format!(
"`{}` might need a bound for `{}`",
lhs_ty, missing_trait
));
} else if let ty::Param(p) = lhs_ty.kind {
suggest_constraining_param(
self.tcx,
self.body_id,
&mut err,
lhs_ty,
rhs_ty,
missing_trait,
p,
false,
);
} else if !suggested_deref {
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
}
@ -330,46 +336,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}
IsAssign::No => {
let (message, missing_trait) = match op.node {
let (message, missing_trait, use_output) = match op.node {
hir::BinOpKind::Add => (
format!("cannot add `{}` to `{}`", rhs_ty, lhs_ty),
Some("std::ops::Add"),
true,
),
hir::BinOpKind::Sub => (
format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty),
Some("std::ops::Sub"),
true,
),
hir::BinOpKind::Mul => (
format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty),
Some("std::ops::Mul"),
true,
),
hir::BinOpKind::Div => (
format!("cannot divide `{}` by `{}`", lhs_ty, rhs_ty),
Some("std::ops::Div"),
true,
),
hir::BinOpKind::Rem => (
format!("cannot mod `{}` by `{}`", lhs_ty, rhs_ty),
Some("std::ops::Rem"),
true,
),
hir::BinOpKind::BitAnd => (
format!("no implementation for `{} & {}`", lhs_ty, rhs_ty),
Some("std::ops::BitAnd"),
true,
),
hir::BinOpKind::BitXor => (
format!("no implementation for `{} ^ {}`", lhs_ty, rhs_ty),
Some("std::ops::BitXor"),
true,
),
hir::BinOpKind::BitOr => (
format!("no implementation for `{} | {}`", lhs_ty, rhs_ty),
Some("std::ops::BitOr"),
true,
),
hir::BinOpKind::Shl => (
format!("no implementation for `{} << {}`", lhs_ty, rhs_ty),
Some("std::ops::Shl"),
true,
),
hir::BinOpKind::Shr => (
format!("no implementation for `{} >> {}`", lhs_ty, rhs_ty),
Some("std::ops::Shr"),
true,
),
hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
format!(
@ -378,6 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lhs_ty
),
Some("std::cmp::PartialEq"),
false,
),
hir::BinOpKind::Lt
| hir::BinOpKind::Le
@ -389,6 +406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lhs_ty
),
Some("std::cmp::PartialOrd"),
false,
),
_ => (
format!(
@ -397,6 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lhs_ty
),
None,
false,
),
};
let mut err = struct_span_err!(
@ -459,12 +478,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This has nothing here because it means we did string
// concatenation (e.g., "Hello " + "World!"). This means
// we don't want the note in the else clause to be emitted
} else if let ty::Param(_) = lhs_ty.kind {
// FIXME: point to span of param
err.note(&format!(
"`{}` might need a bound for `{}`",
lhs_ty, missing_trait
));
} else if let ty::Param(p) = lhs_ty.kind {
suggest_constraining_param(
self.tcx,
self.body_id,
&mut err,
lhs_ty,
rhs_ty,
missing_trait,
p,
use_output,
);
} else if !suggested_deref && !involves_fn {
suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
}
@ -911,3 +935,43 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra
}
}
}
fn suggest_constraining_param(
tcx: TyCtxt<'_>,
body_id: hir::HirId,
mut err: &mut DiagnosticBuilder<'_>,
lhs_ty: Ty<'_>,
rhs_ty: Ty<'_>,
missing_trait: &str,
p: ty::ParamTy,
set_output: bool,
) {
let hir = tcx.hir();
let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait);
// Try to find the def-id and details for the parameter p. We have only the index,
// so we have to find the enclosing function's def-id, then look through its declared
// generic parameters to get the declaration.
let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
let generics = tcx.generics_of(def_id);
let param_def_id = generics.type_param(&p, tcx).def_id;
if let Some(generics) = param_def_id
.as_local()
.map(|id| hir.as_local_hir_id(id))
.and_then(|id| hir.find(hir.get_parent_item(id)))
.as_ref()
.and_then(|node| node.generics())
{
let output = if set_output { format!("<Output = {}>", rhs_ty) } else { String::new() };
suggest_constraining_type_param(
tcx,
generics,
&mut err,
&format!("{}", lhs_ty),
&format!("{}{}", missing_trait, output),
None,
);
} else {
let span = tcx.def_span(param_def_id);
err.span_label(span, msg);
}
}

View File

@ -1129,9 +1129,36 @@ pub struct IdMap {
map: FxHashMap<String, usize>,
}
fn init_id_map() -> FxHashMap<String, usize> {
let mut map = FxHashMap::default();
// This is the list of IDs used by rustdoc templates.
map.insert("mainThemeStyle".to_owned(), 1);
map.insert("themeStyle".to_owned(), 1);
map.insert("theme-picker".to_owned(), 1);
map.insert("theme-choices".to_owned(), 1);
map.insert("settings-menu".to_owned(), 1);
map.insert("main".to_owned(), 1);
map.insert("search".to_owned(), 1);
map.insert("crate-search".to_owned(), 1);
map.insert("render-detail".to_owned(), 1);
map.insert("toggle-all-docs".to_owned(), 1);
map.insert("all-types".to_owned(), 1);
// This is the list of IDs used by rustdoc sections.
map.insert("fields".to_owned(), 1);
map.insert("variants".to_owned(), 1);
map.insert("implementors-list".to_owned(), 1);
map.insert("synthetic-implementors-list".to_owned(), 1);
map.insert("implementations".to_owned(), 1);
map.insert("trait-implementations".to_owned(), 1);
map.insert("synthetic-implementations".to_owned(), 1);
map.insert("blanket-implementations".to_owned(), 1);
map.insert("deref-methods".to_owned(), 1);
map
}
impl IdMap {
pub fn new() -> Self {
IdMap::default()
IdMap { map: init_id_map() }
}
pub fn populate<I: IntoIterator<Item = String>>(&mut self, ids: I) {
@ -1141,7 +1168,7 @@ impl IdMap {
}
pub fn reset(&mut self) {
self.map = FxHashMap::default();
self.map = init_id_map();
}
pub fn derive(&mut self, candidate: String) -> String {

View File

@ -29,8 +29,8 @@ fn test_unique_id() {
"examples-2",
"method.into_iter-1",
"foo-1",
"main",
"search",
"main-1",
"search-1",
"methods",
"examples-3",
"method.into_iter-2",
@ -191,8 +191,8 @@ fn test_header_ids_multiple_blocks() {
t(
&mut map,
"# Main",
"<h1 id=\"main\" class=\"section-header\">\
<a href=\"#main\">Main</a></h1>",
"<h1 id=\"main-1\" class=\"section-header\">\
<a href=\"#main-1\">Main</a></h1>",
);
t(
&mut map,

View File

@ -3413,8 +3413,8 @@ fn render_assoc_items(
write!(
w,
"\
<h2 id='methods' class='small-section-header'>\
Methods<a href='#methods' class='anchor'></a>\
<h2 id='implementations' class='small-section-header'>\
Implementations<a href='#implementations' class='anchor'></a>\
</h2>\
"
);
@ -3475,10 +3475,10 @@ fn render_assoc_items(
write!(
w,
"\
<h2 id='implementations' class='small-section-header'>\
Trait Implementations<a href='#implementations' class='anchor'></a>\
<h2 id='trait-implementations' class='small-section-header'>\
Trait Implementations<a href='#trait-implementations' class='anchor'></a>\
</h2>\
<div id='implementations-list'>{}</div>",
<div id='trait-implementations-list'>{}</div>",
impls
);
}
@ -4097,8 +4097,8 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
ret.sort();
if !ret.is_empty() {
out.push_str(&format!(
"<a class=\"sidebar-title\" href=\"#methods\">Methods\
</a><div class=\"sidebar-links\">{}</div>",
"<a class=\"sidebar-title\" href=\"#implementations\">Methods</a>\
<div class=\"sidebar-links\">{}</div>",
ret.join("")
));
}
@ -4191,8 +4191,8 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
if !concrete_format.is_empty() {
out.push_str(
"<a class=\"sidebar-title\" href=\"#implementations\">\
Trait Implementations</a>",
"<a class=\"sidebar-title\" href=\"#trait-implementations\">\
Trait Implementations</a>",
);
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", concrete_format));
}
@ -4200,7 +4200,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
if !synthetic_format.is_empty() {
out.push_str(
"<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\
Auto Trait Implementations</a>",
Auto Trait Implementations</a>",
);
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", synthetic_format));
}
@ -4208,7 +4208,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
if !blanket_format.is_empty() {
out.push_str(
"<a class=\"sidebar-title\" href=\"#blanket-implementations\">\
Blanket Implementations</a>",
Blanket Implementations</a>",
);
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", blanket_format));
}

View File

@ -2180,7 +2180,7 @@ function getSearchElement() {
if (collapse) {
toggleAllDocs(pageId, true);
} else if (getCurrentValue("rustdoc-auto-hide-trait-implementations") !== "false") {
var impl_list = document.getElementById("implementations-list");
var impl_list = document.getElementById("trait-implementations-list");
if (impl_list !== null) {
onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {

View File

@ -11,7 +11,7 @@ pub struct Simd<T, const WIDTH: usize> {
inner: T,
}
// @has foo/struct.Simd.html '//div[@id="implementations-list"]/h3/code' 'impl Add<Simd<u8, 16usize>> for Simd<u8, 16>'
// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]/h3/code' 'impl Add<Simd<u8, 16usize>> for Simd<u8, 16>'
impl Add for Simd<u8, 16> {
type Output = Self;

View File

@ -1,7 +1,7 @@
// @has issue_33054/impls/struct.Foo.html
// @has - '//code' 'impl Foo'
// @has - '//code' 'impl Bar for Foo'
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="main"]/*[@class="impl"]' 1
// @has issue_33054/impls/bar/trait.Bar.html
// @has - '//code' 'impl Bar for Foo'

View File

@ -7,5 +7,5 @@ mod inner {
pub trait Blah { }
// @count issue_21474/struct.What.html \
// '//*[@id="implementations-list"]/*[@class="impl"]' 1
// '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
pub struct What;

View File

@ -4,12 +4,12 @@ pub trait Bar<T, U> {}
// @has 'foo/struct.Foo1.html'
pub struct Foo1;
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
// @has - '//*[@class="impl"]' "impl Bar<Foo1, &'static Foo1> for Foo1"
impl Bar<Foo1, &'static Foo1> for Foo1 {}
// @has 'foo/struct.Foo2.html'
pub struct Foo2;
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
// @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8"
impl Bar<&'static Foo2, Foo2> for u8 {}

View File

@ -1,8 +1,8 @@
#![feature(negative_impls)]
// @has issue_55321/struct.A.html
// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' "impl !Send for A"
// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' "impl !Sync for A"
// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Send for A"
// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Sync for A"
pub struct A();
impl !Send for A {}

View File

@ -4,6 +4,6 @@
pub struct Foo;
// @has foo/struct.Foo.html
// @has - '//*[@class="sidebar-title"][@href="#implementations"]' 'Trait Implementations'
// @has - '//*[@class="sidebar-title"][@href="#trait-implementations"]' 'Trait Implementations'
// @has - '//*[@class="sidebar-links"]/a' '!Sync'
impl !Sync for Foo {}

View File

@ -0,0 +1,9 @@
#![crate_name = "foo"]
pub struct Struc;
// @has foo/struct.Struc.html
// @has - '//*[@id="main"]/h2[@id="implementations"]' "Implementations"
impl Struc {
pub const S: u64 = 0;
}

View File

@ -2,10 +2,10 @@
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl<T> Sync for \
// Foo<T> where T: Sync'
//
// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' \
// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' \
// 'impl<T> Send for Foo<T>'
//
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 4
pub struct Foo<T> {
field: T,

View File

@ -13,8 +13,8 @@ impl MyStruct {
// @has - '//*[@class="impl"]//code' 'impl MyTrait for MyAlias'
// @has - 'Alias docstring'
// @has - '//*[@class="sidebar"]//p[@class="location"]' 'Type Definition MyAlias'
// @has - '//*[@class="sidebar"]//a[@href="#methods"]' 'Methods'
// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Trait Implementations'
// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Methods'
// @has - '//*[@class="sidebar"]//a[@href="#trait-implementations"]' 'Trait Implementations'
/// Alias docstring
pub type MyAlias = MyStruct;

View File

@ -104,6 +104,14 @@ const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u
// bad trait object
const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) };
//~^ ERROR it is undefined behavior to use this value
const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
//~^ ERROR it is undefined behavior to use this value
const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
//~^ ERROR it is undefined behavior to use this value
const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
//~^ ERROR it is undefined behavior to use this value
const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) };
//~^ ERROR it is undefined behavior to use this value
// bad data *inside* the trait object
const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };

View File

@ -138,7 +138,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:99:1
|
LL | const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
@ -146,7 +146,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:102:1
|
LL | const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
@ -154,46 +154,78 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:105:1
|
LL | const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:107:1
|
LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:109:1
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:111:1
|
LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:113:1
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:117:1
|
LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.<dyn-downcast>, but expected a boolean
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:113:1
--> $DIR/ub-wide-ptr.rs:121:1
|
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:115:1
--> $DIR/ub-wide-ptr.rs:123:1
|
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer in wide pointer or too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
error[E0080]: could not evaluate static initializer
--> $DIR/ub-wide-ptr.rs:121:5
--> $DIR/ub-wide-ptr.rs:129:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer
error[E0080]: could not evaluate static initializer
--> $DIR/ub-wide-ptr.rs:125:5
--> $DIR/ub-wide-ptr.rs:133:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N
error: aborting due to 24 previous errors
error: aborting due to 28 previous errors
For more information about this error, try `rustc --explain E0080`.

View File

@ -2,11 +2,16 @@ error[E0271]: type mismatch resolving `for<'a> <<T as Baz>::Baa<'a> as std::ops:
--> $DIR/construct_with_other_type.rs:19:9
|
LL | impl<T> Baz for T where T: Foo {
| ^^^ expected type parameter `T`, found associated type
| - ^^^ expected type parameter `T`, found associated type
| |
| this type parameter
|
= note: expected associated type `<T as Foo>::Bar<'_, 'static>`
found associated type `<<T as Baz>::Quux<'_> as Foo>::Bar<'_, 'static>`
= note: you might be missing a type parameter or trait bound
help: consider further restricting this bound
|
LL | impl<T> Baz for T where T: Foo + Baz<Quux = T> {
| ^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,46 @@
// run-rustfix
use std::ops::Add;
struct A<B>(B);
impl<B> Add for A<B> where B: Add + std::ops::Add<Output = B> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
A(self.0 + rhs.0) //~ ERROR mismatched types
}
}
struct C<B>(B);
impl<B: Add + std::ops::Add<Output = B>> Add for C<B> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0) //~ ERROR mismatched types
}
}
struct D<B>(B);
impl<B: std::ops::Add<Output = B>> Add for D<B> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
}
}
struct E<B>(B);
impl<B: Add> Add for E<B> where B: Add<Output = B>, B: std::ops::Add<Output = B> {
//~^ ERROR equality constraints are not yet supported in `where` clauses
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0) //~ ERROR mismatched types
}
}
fn main() {}

View File

@ -0,0 +1,46 @@
// run-rustfix
use std::ops::Add;
struct A<B>(B);
impl<B> Add for A<B> where B: Add {
type Output = Self;
fn add(self, rhs: Self) -> Self {
A(self.0 + rhs.0) //~ ERROR mismatched types
}
}
struct C<B>(B);
impl<B: Add> Add for C<B> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0) //~ ERROR mismatched types
}
}
struct D<B>(B);
impl<B> Add for D<B> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
}
}
struct E<B>(B);
impl<B: Add> Add for E<B> where <B as Add>::Output = B {
//~^ ERROR equality constraints are not yet supported in `where` clauses
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0) //~ ERROR mismatched types
}
}
fn main() {}

View File

@ -0,0 +1,77 @@
error: equality constraints are not yet supported in `where` clauses
--> $DIR/missing-bounds.rs:37:33
|
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
| ^^^^^^^^^^^^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
help: if `Output` is an associated type you're trying to set, use the associated type binding syntax
|
LL | impl<B: Add> Add for E<B> where B: Add<Output = B> {
| ^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/missing-bounds.rs:11:11
|
LL | impl<B> Add for A<B> where B: Add {
| - this type parameter
...
LL | A(self.0 + rhs.0)
| ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
|
= note: expected type parameter `B`
found associated type `<B as std::ops::Add>::Output`
help: consider further restricting this bound
|
LL | impl<B> Add for A<B> where B: Add + std::ops::Add<Output = B> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/missing-bounds.rs:21:14
|
LL | impl<B: Add> Add for C<B> {
| - this type parameter
...
LL | Self(self.0 + rhs.0)
| ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
|
= note: expected type parameter `B`
found associated type `<B as std::ops::Add>::Output`
help: consider further restricting this bound
|
LL | impl<B: Add + std::ops::Add<Output = B>> Add for C<B> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0369]: cannot add `B` to `B`
--> $DIR/missing-bounds.rs:31:21
|
LL | Self(self.0 + rhs.0)
| ------ ^ ----- B
| |
| B
|
help: consider restricting type parameter `B`
|
LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/missing-bounds.rs:42:14
|
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
| - this type parameter
...
LL | Self(self.0 + rhs.0)
| ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
|
= note: expected type parameter `B`
found associated type `<B as std::ops::Add>::Output`
help: consider further restricting type parameter `B`
|
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B, B: std::ops::Add<Output = B> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0308, E0369.
For more information about an error, try `rustc --explain E0308`.

View File

@ -5,12 +5,18 @@ LL | trait From<Src> {
| --- required by this bound in `From`
...
LL | ) -> <Dst as From<Self>>::Result where Dst: From<Self> {
| ^^^^^^^^^^- help: consider further restricting `Self`: `, Self: std::marker::Sized`
| |
| doesn't have a size known at compile-time
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Self`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
help: consider further restricting `Self`
|
LL | ) -> <Dst as From<Self>>::Result where Dst: From<Self>, Self: std::marker::Sized {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider relaxing the implicit `Sized` restriction
|
LL | trait From<Src: ?Sized> {
| ^^^^^^^^
error: aborting due to previous error

View File

@ -7,7 +7,9 @@ LL | type A: MultiDispatch<Self::B, O = Self>;
| -------- required by this bound in `Trait`
...
LL | fn test<T: Trait<B=i32>>(b: i32) -> T where T::A: MultiDispatch<i32> { T::new(b) }
| ^^^^^^^^^^^^ expected type parameter `T`, found associated type
| - ^^^^^^^^^^^^ expected type parameter `T`, found associated type
| |
| this type parameter
|
= note: expected type parameter `T`
found associated type `<<T as Trait>::A as MultiDispatch<i32>>::O`

View File

@ -6,7 +6,10 @@ LL | self.x += v.x;
| |
| cannot use `+=` on type `T`
|
= note: `T` might need a bound for `std::ops::AddAssign`
help: consider restricting type parameter `T`
|
LL | impl<T: std::ops::AddAssign> Foo<T> {
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -6,7 +6,10 @@ LL | let z = x + y;
| |
| T
|
= note: `T` might need a bound for `std::ops::Add`
help: consider restricting type parameter `T`
|
LL | fn foo<T: std::ops::Add<Output = T>>(x: T, y: T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
--> $DIR/missing_trait_impl.rs:9:5
@ -16,7 +19,10 @@ LL | x += x;
| |
| cannot use `+=` on type `T`
|
= note: `T` might need a bound for `std::ops::AddAssign`
help: consider restricting type parameter `T`
|
LL | fn bar<T: std::ops::AddAssign>(x: T) {
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors