mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-16 17:03:35 +00:00
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:
commit
29457dd92c
@ -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) {
|
||||
|
@ -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 \
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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 ¶m_spans[..] {
|
||||
&[¶m_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
|
||||
}
|
||||
}
|
||||
|
@ -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)) => {
|
||||
|
@ -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};
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 ¶m_spans[..] {
|
||||
&[¶m_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
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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;
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
@ -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 {}
|
||||
|
9
src/test/rustdoc/struct-implementations-title.rs
Normal file
9
src/test/rustdoc/struct-implementations-title.rs
Normal 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;
|
||||
}
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) };
|
||||
|
@ -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`.
|
||||
|
@ -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
|
||||
|
||||
|
46
src/test/ui/generic-associated-types/missing-bounds.fixed
Normal file
46
src/test/ui/generic-associated-types/missing-bounds.fixed
Normal 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() {}
|
46
src/test/ui/generic-associated-types/missing-bounds.rs
Normal file
46
src/test/ui/generic-associated-types/missing-bounds.rs
Normal 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() {}
|
77
src/test/ui/generic-associated-types/missing-bounds.stderr
Normal file
77
src/test/ui/generic-associated-types/missing-bounds.stderr
Normal 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`.
|
@ -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
|
||||
|
||||
|
@ -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`
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user