]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #76599 - hameerabbasi:const-generics-revs, r=lcnr
authorRalf Jung <post@ralfj.de>
Sat, 12 Sep 2020 08:43:22 +0000 (10:43 +0200)
committerGitHub <noreply@github.com>
Sat, 12 Sep 2020 08:43:22 +0000 (10:43 +0200)
Finish off revisions for const generics UI tests.

This time it really does fix it. 😅 The only ones left are `min-and-full-same-time.rs`, which doesn't need it, and `array-impls/` which check the feature indirectly.

Fixes #75279.

r? @lcnr

69 files changed:
compiler/rustc_hir/src/def.rs
compiler/rustc_hir/src/lib.rs
compiler/rustc_resolve/src/build_reduced_graph.rs
compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/late/diagnostics.rs
compiler/rustc_resolve/src/lib.rs
compiler/rustc_typeck/src/astconv/generics.rs
config.toml.example
library/alloc/src/rc.rs
library/alloc/src/slice.rs
library/alloc/src/vec.rs
library/core/src/array/iter.rs
library/core/src/future/mod.rs
library/core/src/future/pending.rs
library/core/src/future/ready.rs
library/core/src/lib.rs
library/core/src/mem/maybe_uninit.rs
library/core/src/slice/mod.rs
library/core/src/task/ready.rs
library/core/src/time.rs
library/core/tests/lib.rs
library/core/tests/slice.rs
library/core/tests/time.rs
library/std/src/future.rs
library/std/src/lib.rs
library/std/src/os/linux/fs.rs
library/std/src/os/linux/mod.rs
library/std/src/os/linux/raw.rs
library/std/src/sys/unix/ext/ffi.rs
library/std/src/sys/unix/ext/io.rs
library/std/src/sys/unix/ext/net.rs
library/std/src/sys/unix/ext/net/tests.rs
library/std/src/sys/unix/ext/raw.rs
library/std/src/sys/windows/ext/io.rs
library/std/src/sys/windows/ext/raw.rs
src/bootstrap/builder.rs
src/bootstrap/builder/tests.rs
src/bootstrap/compile.rs
src/bootstrap/config.rs
src/bootstrap/dist.rs
src/bootstrap/flags.rs
src/bootstrap/test.rs
src/bootstrap/tool.rs
src/librustdoc/clean/mod.rs
src/librustdoc/passes/collect_intra_doc_links.rs
src/test/rustdoc-ui/assoc-item-not-in-scope.stderr
src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr
src/test/rustdoc-ui/intra-doc-alias-ice.stderr
src/test/rustdoc-ui/intra-link-errors.rs [new file with mode: 0644]
src/test/rustdoc-ui/intra-link-errors.stderr [new file with mode: 0644]
src/test/rustdoc-ui/intra-link-prim-conflict.rs
src/test/rustdoc-ui/intra-link-prim-conflict.stderr
src/test/rustdoc-ui/intra-link-span-ice-55723.stderr
src/test/rustdoc-ui/intra-links-ambiguity.stderr
src/test/rustdoc-ui/intra-links-anchors.stderr
src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
src/test/rustdoc-ui/intra-links-warning-crlf.stderr
src/test/rustdoc-ui/intra-links-warning.stderr
src/test/rustdoc-ui/lint-group.stderr
src/test/rustdoc/const-generics/type-alias.rs [new file with mode: 0644]
src/test/ui/const-generics/invalid-enum.rs [new file with mode: 0644]
src/test/ui/const-generics/invalid-enum.stderr [new file with mode: 0644]
src/test/ui/consts/duration-consts-2.rs
src/test/ui/issues/auxiliary/issue-75907.rs [new file with mode: 0644]
src/test/ui/issues/issue-75907.rs [new file with mode: 0644]
src/test/ui/issues/issue-75907.stderr [new file with mode: 0644]
src/test/ui/issues/issue-75907_b.rs [new file with mode: 0644]
src/test/ui/issues/issue-75907_b.stderr [new file with mode: 0644]

index 0d61dc037c65a56f6eaaa64542df6896b88b629c..b019e518d0c5457b28c342d416663960dc05a23f 100644 (file)
@@ -6,6 +6,7 @@
 use rustc_macros::HashStable_Generic;
 use rustc_span::hygiene::MacroKind;
 
+use std::array::IntoIter;
 use std::fmt::Debug;
 
 /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct.
@@ -291,6 +292,14 @@ impl<T> PerNS<T> {
     pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
         PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
     }
+
+    pub fn into_iter(self) -> IntoIter<T, 3> {
+        IntoIter::new([self.value_ns, self.type_ns, self.macro_ns])
+    }
+
+    pub fn iter(&self) -> IntoIter<&T, 3> {
+        IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns])
+    }
 }
 
 impl<T> ::std::ops::Index<Namespace> for PerNS<T> {
index c69a9b063aeca618cd943422ae4e94ff8ecd409d..9d931b3a9e1e561cff533c98b91c0e6ca220c852 100644 (file)
@@ -2,6 +2,7 @@
 //!
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
 
+#![feature(array_value_iter)]
 #![feature(crate_visibility_modifier)]
 #![feature(const_fn)] // For the unsizing cast on `&[]`
 #![feature(const_panic)]
index 03c2915f8486b233578af3e07909341748f6112f..565313902a42ee1c89eea8c126573085947ef006 100644 (file)
@@ -796,23 +796,26 @@ fn build_reduced_graph_for_item(&mut self, item: &'b Item) {
                         vis
                     };
 
+                    let mut ret_fields = Vec::with_capacity(vdata.fields().len());
+
                     for field in vdata.fields() {
                         // NOTE: The field may be an expansion placeholder, but expansion sets
                         // correct visibilities for unnamed field placeholders specifically, so the
                         // constructor visibility should still be determined correctly.
-                        if let Ok(field_vis) = self.resolve_visibility_speculative(&field.vis, true)
-                        {
-                            if ctor_vis.is_at_least(field_vis, &*self.r) {
-                                ctor_vis = field_vis;
-                            }
+                        let field_vis = self
+                            .resolve_visibility_speculative(&field.vis, true)
+                            .unwrap_or(ty::Visibility::Public);
+                        if ctor_vis.is_at_least(field_vis, &*self.r) {
+                            ctor_vis = field_vis;
                         }
+                        ret_fields.push(field_vis);
                     }
                     let ctor_res = Res::Def(
                         DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)),
                         self.r.local_def_id(ctor_node_id).to_def_id(),
                     );
                     self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
-                    self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis));
+                    self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields));
                 }
             }
 
@@ -964,7 +967,7 @@ fn build_reduced_graph_for_external_crate_res(&mut self, child: Export<NodeId>)
             Res::Def(DefKind::Ctor(CtorOf::Struct, ..), def_id) => {
                 let parent = cstore.def_key(def_id).parent;
                 if let Some(struct_def_id) = parent.map(|index| DefId { index, ..def_id }) {
-                    self.r.struct_constructors.insert(struct_def_id, (res, vis));
+                    self.r.struct_constructors.insert(struct_def_id, (res, vis, vec![]));
                 }
             }
             _ => {}
index dda084214b8f4097f671e890df82034ea1e08547..07f36c7b7ad36bef645c3a77cb9afc373dedbcf9 100644 (file)
@@ -188,7 +188,7 @@ fn new(kind: RibKind<'a>) -> Rib<'a, R> {
     // Paths in struct expressions and patterns `Path { .. }`.
     Struct,
     // Paths in tuple struct patterns `Path(..)`.
-    TupleStruct(Span),
+    TupleStruct(Span, &'a [Span]),
     // `m::A::B` in `<T as m::A>::B::C`.
     TraitItem(Namespace),
 }
@@ -197,7 +197,7 @@ impl<'a> PathSource<'a> {
     fn namespace(self) -> Namespace {
         match self {
             PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS,
-            PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(_) => ValueNS,
+            PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS,
             PathSource::TraitItem(ns) => ns,
         }
     }
@@ -208,7 +208,7 @@ fn defer_to_typeck(self) -> bool {
             | PathSource::Expr(..)
             | PathSource::Pat
             | PathSource::Struct
-            | PathSource::TupleStruct(_) => true,
+            | PathSource::TupleStruct(..) => true,
             PathSource::Trait(_) | PathSource::TraitItem(..) => false,
         }
     }
@@ -219,7 +219,7 @@ fn descr_expected(self) -> &'static str {
             PathSource::Trait(_) => "trait",
             PathSource::Pat => "unit struct, unit variant or constant",
             PathSource::Struct => "struct, variant or union type",
-            PathSource::TupleStruct(_) => "tuple struct or tuple variant",
+            PathSource::TupleStruct(..) => "tuple struct or tuple variant",
             PathSource::TraitItem(ns) => match ns {
                 TypeNS => "associated type",
                 ValueNS => "method or associated constant",
@@ -305,7 +305,7 @@ fn is_call(self) -> bool {
                 | Res::SelfCtor(..) => true,
                 _ => false,
             },
-            PathSource::TupleStruct(_) => match res {
+            PathSource::TupleStruct(..) => match res {
                 Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true,
                 _ => false,
             },
@@ -340,8 +340,8 @@ fn error_code(self, has_unexpected_resolution: bool) -> DiagnosticId {
             (PathSource::Struct, false) => error_code!(E0422),
             (PathSource::Expr(..), true) => error_code!(E0423),
             (PathSource::Expr(..), false) => error_code!(E0425),
-            (PathSource::Pat | PathSource::TupleStruct(_), true) => error_code!(E0532),
-            (PathSource::Pat | PathSource::TupleStruct(_), false) => error_code!(E0531),
+            (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532),
+            (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531),
             (PathSource::TraitItem(..), true) => error_code!(E0575),
             (PathSource::TraitItem(..), false) => error_code!(E0576),
         }
@@ -411,7 +411,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
 }
 
 /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
-impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
+impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     fn visit_item(&mut self, item: &'ast Item) {
         let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item));
         // Always report errors in items we just entered.
@@ -659,7 +659,7 @@ fn visit_generic_arg(&mut self, arg: &'ast GenericArg) {
     }
 }
 
-impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
+impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> {
         // During late resolution we only track the module component of the parent scope,
         // although it may be useful to track other components as well for diagnostics.
@@ -1539,8 +1539,16 @@ fn resolve_pattern_inner(
                         .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
                     self.r.record_partial_res(pat.id, PartialRes::new(res));
                 }
-                PatKind::TupleStruct(ref path, ..) => {
-                    self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct(pat.span));
+                PatKind::TupleStruct(ref path, ref sub_patterns) => {
+                    self.smart_resolve_path(
+                        pat.id,
+                        None,
+                        path,
+                        PathSource::TupleStruct(
+                            pat.span,
+                            self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)),
+                        ),
+                    );
                 }
                 PatKind::Path(ref qself, ref path) => {
                     self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat);
index 9f631705b2bf8d7ea97eeed71a21ccc51bf481b5..9b5650c260c220949b45b32ff0a5f3ebf9b657c6 100644 (file)
@@ -90,7 +90,7 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
     (variant_path_string, enum_path_string)
 }
 
-impl<'a> LateResolutionVisitor<'a, '_, '_> {
+impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     fn def_span(&self, def_id: DefId) -> Option<Span> {
         match def_id.krate {
             LOCAL_CRATE => self.r.opt_span(def_id),
@@ -623,12 +623,12 @@ fn smart_resolve_context_dependent_help(
                         );
                     }
                 }
-                PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => {
+                PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
                     let span = match &source {
                         PathSource::Expr(Some(Expr {
                             span, kind: ExprKind::Call(_, _), ..
                         }))
-                        | PathSource::TupleStruct(span) => {
+                        | PathSource::TupleStruct(span, _) => {
                             // We want the main underline to cover the suggested code as well for
                             // cleaner output.
                             err.set_span(*span);
@@ -640,7 +640,7 @@ fn smart_resolve_context_dependent_help(
                         err.span_label(span, &format!("`{}` defined here", path_str));
                     }
                     let (tail, descr, applicability) = match source {
-                        PathSource::Pat | PathSource::TupleStruct(_) => {
+                        PathSource::Pat | PathSource::TupleStruct(..) => {
                             ("", "pattern", Applicability::MachineApplicable)
                         }
                         _ => (": val", "literal", Applicability::HasPlaceholders),
@@ -705,7 +705,7 @@ fn smart_resolve_context_dependent_help(
             }
             (
                 Res::Def(DefKind::Enum, def_id),
-                PathSource::TupleStruct(_) | PathSource::Expr(..),
+                PathSource::TupleStruct(..) | PathSource::Expr(..),
             ) => {
                 if self
                     .diagnostic_metadata
@@ -745,15 +745,50 @@ fn smart_resolve_context_dependent_help(
                 }
             }
             (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => {
-                if let Some((ctor_def, ctor_vis)) = self.r.struct_constructors.get(&def_id).cloned()
+                if let Some((ctor_def, ctor_vis, fields)) =
+                    self.r.struct_constructors.get(&def_id).cloned()
                 {
                     let accessible_ctor =
                         self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
                     if is_expected(ctor_def) && !accessible_ctor {
-                        err.span_label(
-                            span,
-                            "constructor is not visible here due to private fields".to_string(),
-                        );
+                        let mut better_diag = false;
+                        if let PathSource::TupleStruct(_, pattern_spans) = source {
+                            if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() {
+                                let non_visible_spans: Vec<Span> = fields
+                                    .iter()
+                                    .zip(pattern_spans.iter())
+                                    .filter_map(|(vis, span)| {
+                                        match self
+                                            .r
+                                            .is_accessible_from(*vis, self.parent_scope.module)
+                                        {
+                                            true => None,
+                                            false => Some(*span),
+                                        }
+                                    })
+                                    .collect();
+                                // Extra check to be sure
+                                if non_visible_spans.len() > 0 {
+                                    let mut m: rustc_span::MultiSpan =
+                                        non_visible_spans.clone().into();
+                                    non_visible_spans.into_iter().for_each(|s| {
+                                        m.push_span_label(s, "private field".to_string())
+                                    });
+                                    err.span_note(
+                                        m,
+                                        "constructor is not visible here due to private fields",
+                                    );
+                                    better_diag = true;
+                                }
+                            }
+                        }
+
+                        if !better_diag {
+                            err.span_label(
+                                span,
+                                "constructor is not visible here due to private fields".to_string(),
+                            );
+                        }
                     }
                 } else {
                     bad_struct_syntax_suggestion(def_id);
index c7913e844553113c090b68b0c86794fd5bf187e1..0f5b9b518163f899d5fd5c0540fcface5b0d4915 100644 (file)
@@ -1005,7 +1005,8 @@ pub struct Resolver<'a> {
 
     /// Table for mapping struct IDs into struct constructor IDs,
     /// it's not used during normal resolution, only for better error reporting.
-    struct_constructors: DefIdMap<(Res, ty::Visibility)>,
+    /// Also includes of list of each fields visibility
+    struct_constructors: DefIdMap<(Res, ty::Visibility, Vec<ty::Visibility>)>,
 
     /// Features enabled for this crate.
     active_features: FxHashSet<Symbol>,
@@ -1042,6 +1043,7 @@ pub struct ResolverArenas<'a> {
     name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
     macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>,
     ast_paths: TypedArena<ast::Path>,
+    pattern_spans: TypedArena<Span>,
 }
 
 impl<'a> ResolverArenas<'a> {
@@ -1073,6 +1075,9 @@ fn alloc_macro_rules_binding(
     fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] {
         self.ast_paths.alloc_from_iter(paths.iter().cloned())
     }
+    fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] {
+        self.pattern_spans.alloc_from_iter(spans)
+    }
 }
 
 impl<'a> AsMut<Resolver<'a>> for Resolver<'a> {
index a434406c284ac09b5307d980bb31c2759177c12c..b54de1d0916085011ce2734d8af1da2f1e5b0872 100644 (file)
@@ -3,7 +3,7 @@
 };
 use crate::errors::AssocTypeBindingNotAllowed;
 use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{GenericArg, GenericArgs};
@@ -368,7 +368,7 @@ pub(crate) fn check_generic_arg_count(
         }
 
         if position != GenericArgPosition::Type && !args.bindings.is_empty() {
-            Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
+            AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
         }
 
         let explicit_late_bound =
@@ -393,7 +393,7 @@ pub(crate) fn check_generic_arg_count(
             }
 
             if silent {
-                return Err(true);
+                return Err((0i32, None));
             }
 
             // Unfortunately lifetime and type parameter mismatches are typically styled
@@ -442,16 +442,16 @@ pub(crate) fn check_generic_arg_count(
             for span in spans {
                 err.span_label(span, label.as_str());
             }
-            err.emit();
 
-            Err(true)
+            assert_ne!(bound, provided);
+            Err((bound as i32 - provided as i32, Some(err)))
         };
 
-        let mut arg_count_correct = Ok(());
         let mut unexpected_spans = vec![];
 
+        let mut lifetime_count_correct = Ok(());
         if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
-            arg_count_correct = check_kind_count(
+            lifetime_count_correct = check_kind_count(
                 "lifetime",
                 param_counts.lifetimes,
                 param_counts.lifetimes,
@@ -459,12 +459,13 @@ pub(crate) fn check_generic_arg_count(
                 0,
                 &mut unexpected_spans,
                 explicit_late_bound == ExplicitLateBound::Yes,
-            )
-            .and(arg_count_correct);
+            );
         }
+
         // FIXME(const_generics:defaults)
+        let mut const_count_correct = Ok(());
         if !infer_args || arg_counts.consts > param_counts.consts {
-            arg_count_correct = check_kind_count(
+            const_count_correct = check_kind_count(
                 "const",
                 param_counts.consts,
                 param_counts.consts,
@@ -472,13 +473,14 @@ pub(crate) fn check_generic_arg_count(
                 arg_counts.lifetimes + arg_counts.types,
                 &mut unexpected_spans,
                 false,
-            )
-            .and(arg_count_correct);
+            );
         }
+
         // Note that type errors are currently be emitted *after* const errors.
+        let mut type_count_correct = Ok(());
         if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize
         {
-            arg_count_correct = check_kind_count(
+            type_count_correct = check_kind_count(
                 "type",
                 param_counts.types - defaults.types - has_self as usize,
                 param_counts.types - has_self as usize,
@@ -486,14 +488,54 @@ pub(crate) fn check_generic_arg_count(
                 arg_counts.lifetimes,
                 &mut unexpected_spans,
                 false,
-            )
-            .and(arg_count_correct);
+            );
         }
 
+        // Emit a help message if it's possible that a type could be surrounded in braces
+        if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct {
+            if let Err((_, Some(ref mut type_err))) = type_count_correct {
+                let possible_matches = args.args[arg_counts.lifetimes..]
+                    .iter()
+                    .filter(|arg| {
+                        matches!(
+                            arg,
+                            GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })
+                        )
+                    })
+                    .take(c_mismatch.max(0) as usize);
+                for arg in possible_matches {
+                    let suggestions = vec![
+                        (arg.span().shrink_to_lo(), String::from("{ ")),
+                        (arg.span().shrink_to_hi(), String::from(" }")),
+                    ];
+                    type_err.multipart_suggestion(
+                        "If this generic argument was intended as a const parameter, \
+                        try surrounding it with braces:",
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+
+        let emit_correct =
+            |correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct {
+                Ok(()) => Ok(()),
+                Err((_, None)) => Err(()),
+                Err((_, Some(mut err))) => {
+                    err.emit();
+                    Err(())
+                }
+            };
+
+        let arg_count_correct = emit_correct(lifetime_count_correct)
+            .and(emit_correct(const_count_correct))
+            .and(emit_correct(type_count_correct));
+
         GenericArgCountResult {
             explicit_late_bound,
-            correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch {
-                reported: if reported_err { Some(ErrorReported) } else { None },
+            correct: arg_count_correct.map_err(|()| GenericArgCountMismatch {
+                reported: Some(ErrorReported),
                 invalid_args: unexpected_spans,
             }),
         }
index c311c933e7edcc5eac249ff5732cf0f535122921..2d5b3136450b923130238a8c1ee2380d7f989124 100644 (file)
 # Defaults to host platform
 #build = "x86_64-unknown-linux-gnu"
 
-# In addition to the build triple, other triples to produce full compiler
-# toolchains for. Each of these triples will be bootstrapped from the build
-# triple and then will continue to bootstrap themselves. This platform must
-# currently be able to run all of the triples provided here.
+# Which triples to produce a compiler toolchain for. Each of these triples will
+# be bootstrapped from the build triple themselves.
 #
 # Defaults to just the build triple
 #host = ["x86_64-unknown-linux-gnu"]
 
-# In addition to all host triples, other triples to produce the standard library
-# for. Each host triple will be used to produce a copy of the standard library
-# for each target triple.
+# Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of
+# these triples will be bootstrapped from the build triple themselves.
 #
-# Defaults to just the build triple
+# Defaults to `host`. If you set this explicitly, you likely want to add all
+# host triples to this list as well in order for those host toolchains to be
+# able to compile programs for their native target.
 #target = ["x86_64-unknown-linux-gnu"]
 
 # Use this directory to store build artifacts.
index a9b293856e57bec38004457908acf0c1a2b5dff3..f998e49dcfcde8dcb4a26c0e9a549ed47b84652e 100644 (file)
@@ -295,6 +295,13 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Rc<U>> for Rc<T> {}
 impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Rc<U>> for Rc<T> {}
 
 impl<T: ?Sized> Rc<T> {
+    #[inline(always)]
+    fn inner(&self) -> &RcBox<T> {
+        // This unsafety is ok because while this Rc is alive we're guaranteed
+        // that the inner pointer is valid.
+        unsafe { self.ptr.as_ref() }
+    }
+
     fn from_inner(ptr: NonNull<RcBox<T>>) -> Self {
         Self { ptr, phantom: PhantomData }
     }
@@ -469,7 +476,7 @@ pub fn try_unwrap(this: Self) -> Result<T, Self> {
                 // the strong count, and then remove the implicit "strong weak"
                 // pointer while also handling drop logic by just crafting a
                 // fake Weak.
-                this.dec_strong();
+                this.inner().dec_strong();
                 let _weak = Weak { ptr: this.ptr };
                 forget(this);
                 Ok(val)
@@ -735,7 +742,7 @@ pub unsafe fn from_raw(ptr: *const T) -> Self {
     /// ```
     #[stable(feature = "rc_weak", since = "1.4.0")]
     pub fn downgrade(this: &Self) -> Weak<T> {
-        this.inc_weak();
+        this.inner().inc_weak();
         // Make sure we do not create a dangling Weak
         debug_assert!(!is_dangling(this.ptr));
         Weak { ptr: this.ptr }
@@ -756,7 +763,7 @@ pub fn downgrade(this: &Self) -> Weak<T> {
     #[inline]
     #[stable(feature = "rc_counts", since = "1.15.0")]
     pub fn weak_count(this: &Self) -> usize {
-        this.weak() - 1
+        this.inner().weak() - 1
     }
 
     /// Gets the number of strong (`Rc`) pointers to this allocation.
@@ -774,7 +781,7 @@ pub fn weak_count(this: &Self) -> usize {
     #[inline]
     #[stable(feature = "rc_counts", since = "1.15.0")]
     pub fn strong_count(this: &Self) -> usize {
-        this.strong()
+        this.inner().strong()
     }
 
     /// Returns `true` if there are no other `Rc` or [`Weak`] pointers to
@@ -844,7 +851,9 @@ pub fn get_mut(this: &mut Self) -> Option<&mut T> {
     #[inline]
     #[unstable(feature = "get_mut_unchecked", issue = "63292")]
     pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
-        unsafe { &mut this.ptr.as_mut().value }
+        // We are careful to *not* create a reference covering the "count" fields, as
+        // this would conflict with accesses to the reference counts (e.g. by `Weak`).
+        unsafe { &mut (*this.ptr.as_ptr()).value }
     }
 
     #[inline]
@@ -931,10 +940,10 @@ pub fn make_mut(this: &mut Self) -> &mut T {
             unsafe {
                 let mut swap = Rc::new(ptr::read(&this.ptr.as_ref().value));
                 mem::swap(this, &mut swap);
-                swap.dec_strong();
+                swap.inner().dec_strong();
                 // Remove implicit strong-weak ref (no need to craft a fake
                 // Weak here -- we know other Weaks can clean up for us)
-                swap.dec_weak();
+                swap.inner().dec_weak();
                 forget(swap);
             }
         }
@@ -1192,16 +1201,16 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc<T> {
     /// ```
     fn drop(&mut self) {
         unsafe {
-            self.dec_strong();
-            if self.strong() == 0 {
+            self.inner().dec_strong();
+            if self.inner().strong() == 0 {
                 // destroy the contained object
-                ptr::drop_in_place(self.ptr.as_mut());
+                ptr::drop_in_place(Self::get_mut_unchecked(self));
 
                 // remove the implicit "strong weak" pointer now that we've
                 // destroyed the contents.
-                self.dec_weak();
+                self.inner().dec_weak();
 
-                if self.weak() == 0 {
+                if self.inner().weak() == 0 {
                     Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
                 }
             }
@@ -1227,7 +1236,7 @@ impl<T: ?Sized> Clone for Rc<T> {
     /// ```
     #[inline]
     fn clone(&self) -> Rc<T> {
-        self.inc_strong();
+        self.inner().inc_strong();
         Self::from_inner(self.ptr)
     }
 }
@@ -1851,6 +1860,13 @@ pub(crate) fn is_dangling<T: ?Sized>(ptr: NonNull<T>) -> bool {
     address == usize::MAX
 }
 
+/// Helper type to allow accessing the reference counts without
+/// making any assertions about the data field.
+struct WeakInner<'a> {
+    weak: &'a Cell<usize>,
+    strong: &'a Cell<usize>,
+}
+
 impl<T: ?Sized> Weak<T> {
     /// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying
     /// dropping of the inner value if successful.
@@ -1910,11 +1926,21 @@ pub fn weak_count(&self) -> usize {
             .unwrap_or(0)
     }
 
-    /// Returns `None` when the pointer is dangling and there is no allocated `RcBox`
+    /// Returns `None` when the pointer is dangling and there is no allocated `RcBox`,
     /// (i.e., when this `Weak` was created by `Weak::new`).
     #[inline]
-    fn inner(&self) -> Option<&RcBox<T>> {
-        if is_dangling(self.ptr) { None } else { Some(unsafe { self.ptr.as_ref() }) }
+    fn inner(&self) -> Option<WeakInner<'_>> {
+        if is_dangling(self.ptr) {
+            None
+        } else {
+            // We are careful to *not* create a reference covering the "data" field, as
+            // the field may be mutated concurrently (for example, if the last `Rc`
+            // is dropped, the data field will be dropped in-place).
+            Some(unsafe {
+                let ptr = self.ptr.as_ptr();
+                WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak }
+            })
+        }
     }
 
     /// Returns `true` if the two `Weak`s point to the same allocation (similar to
@@ -1992,14 +2018,14 @@ impl<T: ?Sized> Drop for Weak<T> {
     /// assert!(other_weak_foo.upgrade().is_none());
     /// ```
     fn drop(&mut self) {
-        if let Some(inner) = self.inner() {
-            inner.dec_weak();
-            // the weak count starts at 1, and will only go to zero if all
-            // the strong pointers have disappeared.
-            if inner.weak() == 0 {
-                unsafe {
-                    Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
-                }
+        let inner = if let Some(inner) = self.inner() { inner } else { return };
+
+        inner.dec_weak();
+        // the weak count starts at 1, and will only go to zero if all
+        // the strong pointers have disappeared.
+        if inner.weak() == 0 {
+            unsafe {
+                Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
             }
         }
     }
@@ -2065,12 +2091,13 @@ fn default() -> Weak<T> {
 // clone these much in Rust thanks to ownership and move-semantics.
 
 #[doc(hidden)]
-trait RcBoxPtr<T: ?Sized> {
-    fn inner(&self) -> &RcBox<T>;
+trait RcInnerPtr {
+    fn weak_ref(&self) -> &Cell<usize>;
+    fn strong_ref(&self) -> &Cell<usize>;
 
     #[inline]
     fn strong(&self) -> usize {
-        self.inner().strong.get()
+        self.strong_ref().get()
     }
 
     #[inline]
@@ -2084,17 +2111,17 @@ fn inc_strong(&self) {
         if strong == 0 || strong == usize::MAX {
             abort();
         }
-        self.inner().strong.set(strong + 1);
+        self.strong_ref().set(strong + 1);
     }
 
     #[inline]
     fn dec_strong(&self) {
-        self.inner().strong.set(self.strong() - 1);
+        self.strong_ref().set(self.strong() - 1);
     }
 
     #[inline]
     fn weak(&self) -> usize {
-        self.inner().weak.get()
+        self.weak_ref().get()
     }
 
     #[inline]
@@ -2108,26 +2135,36 @@ fn inc_weak(&self) {
         if weak == 0 || weak == usize::MAX {
             abort();
         }
-        self.inner().weak.set(weak + 1);
+        self.weak_ref().set(weak + 1);
     }
 
     #[inline]
     fn dec_weak(&self) {
-        self.inner().weak.set(self.weak() - 1);
+        self.weak_ref().set(self.weak() - 1);
     }
 }
 
-impl<T: ?Sized> RcBoxPtr<T> for Rc<T> {
+impl<T: ?Sized> RcInnerPtr for RcBox<T> {
     #[inline(always)]
-    fn inner(&self) -> &RcBox<T> {
-        unsafe { self.ptr.as_ref() }
+    fn weak_ref(&self) -> &Cell<usize> {
+        &self.weak
+    }
+
+    #[inline(always)]
+    fn strong_ref(&self) -> &Cell<usize> {
+        &self.strong
     }
 }
 
-impl<T: ?Sized> RcBoxPtr<T> for RcBox<T> {
+impl<'a> RcInnerPtr for WeakInner<'a> {
     #[inline(always)]
-    fn inner(&self) -> &RcBox<T> {
-        self
+    fn weak_ref(&self) -> &Cell<usize> {
+        self.weak
+    }
+
+    #[inline(always)]
+    fn strong_ref(&self) -> &Cell<usize> {
+        self.strong
     }
 }
 
index 8ea2c6dc859b274c4afa9764e777a7f488f68a77..677bfdd2349ec4c17f2a492a93408b0db0913cfa 100644 (file)
@@ -93,6 +93,8 @@
 
 #[unstable(feature = "array_chunks", issue = "74985")]
 pub use core::slice::ArrayChunks;
+#[unstable(feature = "array_chunks", issue = "74985")]
+pub use core::slice::ArrayChunksMut;
 #[stable(feature = "slice_get_slice", since = "1.28.0")]
 pub use core::slice::SliceIndex;
 #[stable(feature = "from_ref", since = "1.28.0")]
index eba7ffae22c4c90dccc855239ab7a684b0a57a45..baa6c0919defe01c886b5366fc4da9a10893faf0 100644 (file)
@@ -2882,25 +2882,21 @@ impl<T> Iterator for IntoIter<T> {
 
     #[inline]
     fn next(&mut self) -> Option<T> {
-        unsafe {
-            if self.ptr as *const _ == self.end {
-                None
-            } else {
-                if mem::size_of::<T>() == 0 {
-                    // purposefully don't use 'ptr.offset' because for
-                    // vectors with 0-size elements this would return the
-                    // same pointer.
-                    self.ptr = arith_offset(self.ptr as *const i8, 1) as *mut T;
-
-                    // Make up a value of this ZST.
-                    Some(mem::zeroed())
-                } else {
-                    let old = self.ptr;
-                    self.ptr = self.ptr.offset(1);
-
-                    Some(ptr::read(old))
-                }
-            }
+        if self.ptr as *const _ == self.end {
+            None
+        } else if mem::size_of::<T>() == 0 {
+            // purposefully don't use 'ptr.offset' because for
+            // vectors with 0-size elements this would return the
+            // same pointer.
+            self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T };
+
+            // Make up a value of this ZST.
+            Some(unsafe { mem::zeroed() })
+        } else {
+            let old = self.ptr;
+            self.ptr = unsafe { self.ptr.offset(1) };
+
+            Some(unsafe { ptr::read(old) })
         }
     }
 
@@ -2935,22 +2931,18 @@ unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item
 impl<T> DoubleEndedIterator for IntoIter<T> {
     #[inline]
     fn next_back(&mut self) -> Option<T> {
-        unsafe {
-            if self.end == self.ptr {
-                None
-            } else {
-                if mem::size_of::<T>() == 0 {
-                    // See above for why 'ptr.offset' isn't used
-                    self.end = arith_offset(self.end as *const i8, -1) as *mut T;
+        if self.end == self.ptr {
+            None
+        } else if mem::size_of::<T>() == 0 {
+            // See above for why 'ptr.offset' isn't used
+            self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T };
 
-                    // Make up a value of this ZST.
-                    Some(mem::zeroed())
-                } else {
-                    self.end = self.end.offset(-1);
+            // Make up a value of this ZST.
+            Some(unsafe { mem::zeroed() })
+        } else {
+            self.end = unsafe { self.end.offset(-1) };
 
-                    Some(ptr::read(self.end))
-                }
-            }
+            Some(unsafe { ptr::read(self.end) })
         }
     }
 }
index 2e8b6419eea1ef91652cfb3d79a55801b21344ee..cafb002c01a1106433f5e5647adafb3936f524fd 100644 (file)
@@ -103,7 +103,7 @@ fn next(&mut self) -> Option<Self::Item> {
             // dead now (i.e. do not touch). As `idx` was the start of the
             // alive-zone, the alive zone is now `data[alive]` again, restoring
             // all invariants.
-            unsafe { self.data.get_unchecked(idx).read() }
+            unsafe { self.data.get_unchecked(idx).assume_init_read() }
         })
     }
 
@@ -136,7 +136,7 @@ fn next_back(&mut self) -> Option<Self::Item> {
             // dead now (i.e. do not touch). As `idx` was the end of the
             // alive-zone, the alive zone is now `data[alive]` again, restoring
             // all invariants.
-            unsafe { self.data.get_unchecked(idx).read() }
+            unsafe { self.data.get_unchecked(idx).assume_init_read() }
         })
     }
 }
index ddce0fe4b7dc5f8bdf7becdd22d9839267cf22b7..ec343f75f34cecf37f1088e9bfde8b43ddd2cd2d 100644 (file)
@@ -21,9 +21,9 @@
 #[unstable(feature = "into_future", issue = "67644")]
 pub use into_future::IntoFuture;
 
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 pub use pending::{pending, Pending};
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 pub use ready::{ready, Ready};
 
 #[unstable(feature = "future_poll_fn", issue = "72302")]
index 74887b68aa0fadf95eaa9b7504f2ffdd3ae51c5c..c1a4e0cda03209886531aa48653bdda6b0c570e0 100644 (file)
@@ -1,3 +1,4 @@
+use crate::fmt::{self, Debug};
 use crate::future::Future;
 use crate::marker;
 use crate::pin::Pin;
@@ -10,8 +11,7 @@
 /// documentation for more.
 ///
 /// [`pending`]: fn.pending.html
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
-#[derive(Debug)]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 #[must_use = "futures do nothing unless you `.await` or poll them"]
 pub struct Pending<T> {
     _data: marker::PhantomData<T>,
@@ -23,7 +23,6 @@ pub struct Pending<T> {
 /// # Examples
 ///
 /// ```no_run
-/// #![feature(future_readiness_fns)]
 /// use core::future;
 ///
 /// # async fn run() {
@@ -32,12 +31,12 @@ pub struct Pending<T> {
 /// unreachable!();
 /// # }
 /// ```
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 pub fn pending<T>() -> Pending<T> {
     Pending { _data: marker::PhantomData }
 }
 
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 impl<T> Future for Pending<T> {
     type Output = T;
 
@@ -46,10 +45,17 @@ fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<T> {
     }
 }
 
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 impl<T> Unpin for Pending<T> {}
 
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
+impl<T> Debug for Pending<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Pending").finish()
+    }
+}
+
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 impl<T> Clone for Pending<T> {
     fn clone(&self) -> Self {
         pending()
index 31b39d7fb6cd5c8f7b0096c83855e9b6e0c695d0..ddae6cfed4bdb81474f3abf5f83b8a02e835ffd2 100644 (file)
@@ -8,15 +8,15 @@
 /// documentation for more.
 ///
 /// [`ready`]: fn.ready.html
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 #[derive(Debug, Clone)]
 #[must_use = "futures do nothing unless you `.await` or poll them"]
 pub struct Ready<T>(Option<T>);
 
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 impl<T> Unpin for Ready<T> {}
 
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 impl<T> Future for Ready<T> {
     type Output = T;
 
@@ -28,10 +28,13 @@ fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
 
 /// Creates a future that is immediately ready with a value.
 ///
+/// Futures created through this function are functionally similar to those
+/// created through `async {}`. The main difference is that futures created
+/// through this function are named and implement `Unpin`.
+///
 /// # Examples
 ///
 /// ```
-/// #![feature(future_readiness_fns)]
 /// use core::future;
 ///
 /// # async fn run() {
@@ -39,7 +42,7 @@ fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
 /// assert_eq!(a.await, 1);
 /// # }
 /// ```
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 pub fn ready<T>(t: T) -> Ready<T> {
     Ready(Some(t))
 }
index 29bbf062f22e4441ee7cc16819b2c491134437e8..c3cadcbb01e31b339071c6d3bb8c12d4ab76bb75 100644 (file)
 #![feature(doc_cfg)]
 #![feature(doc_spotlight)]
 #![feature(duration_consts_2)]
+#![feature(duration_saturating_ops)]
 #![feature(extern_types)]
 #![feature(fundamental)]
 #![feature(intrinsics)]
index 3a7489aa27955870992f6a9031390aeef467fa13..b0ebaa6a12b51186c8dbf433813b8e44225e1329 100644 (file)
@@ -2,6 +2,7 @@
 use crate::fmt;
 use crate::intrinsics;
 use crate::mem::ManuallyDrop;
+use crate::ptr;
 
 /// A wrapper type to construct uninitialized instances of `T`.
 ///
@@ -471,6 +472,8 @@ pub const fn as_mut_ptr(&mut self) -> *mut T {
     /// *immediate* undefined behavior, but will cause undefined behavior with most
     /// safe operations (including dropping it).
     ///
+    /// [`Vec<T>`]: ../../std/vec/struct.Vec.html
+    ///
     /// # Examples
     ///
     /// Correct usage of this method:
@@ -519,8 +522,8 @@ pub unsafe fn assume_init(self) -> T {
     /// this initialization invariant.
     ///
     /// Moreover, this leaves a copy of the same data behind in the `MaybeUninit<T>`. When using
-    /// multiple copies of the data (by calling `read` multiple times, or first
-    /// calling `read` and then [`assume_init`]), it is your responsibility
+    /// multiple copies of the data (by calling `assume_init_read` multiple times, or first
+    /// calling `assume_init_read` and then [`assume_init`]), it is your responsibility
     /// to ensure that that data may indeed be duplicated.
     ///
     /// [inv]: #initialization-invariant
@@ -536,16 +539,16 @@ pub unsafe fn assume_init(self) -> T {
     ///
     /// let mut x = MaybeUninit::<u32>::uninit();
     /// x.write(13);
-    /// let x1 = unsafe { x.read() };
+    /// let x1 = unsafe { x.assume_init_read() };
     /// // `u32` is `Copy`, so we may read multiple times.
-    /// let x2 = unsafe { x.read() };
+    /// let x2 = unsafe { x.assume_init_read() };
     /// assert_eq!(x1, x2);
     ///
     /// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninit();
     /// x.write(None);
-    /// let x1 = unsafe { x.read() };
+    /// let x1 = unsafe { x.assume_init_read() };
     /// // Duplicating a `None` value is okay, so we may read multiple times.
-    /// let x2 = unsafe { x.read() };
+    /// let x2 = unsafe { x.assume_init_read() };
     /// assert_eq!(x1, x2);
     /// ```
     ///
@@ -557,14 +560,14 @@ pub unsafe fn assume_init(self) -> T {
     ///
     /// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninit();
     /// x.write(Some(vec![0,1,2]));
-    /// let x1 = unsafe { x.read() };
-    /// let x2 = unsafe { x.read() };
+    /// let x1 = unsafe { x.assume_init_read() };
+    /// let x2 = unsafe { x.assume_init_read() };
     /// // We now created two copies of the same vector, leading to a double-free ⚠️ when
     /// // they both get dropped!
     /// ```
     #[unstable(feature = "maybe_uninit_extra", issue = "63567")]
     #[inline(always)]
-    pub unsafe fn read(&self) -> T {
+    pub unsafe fn assume_init_read(&self) -> T {
         // SAFETY: the caller must guarantee that `self` is initialized.
         // Reading from `self.as_ptr()` is safe since `self` should be initialized.
         unsafe {
@@ -573,6 +576,34 @@ pub unsafe fn read(&self) -> T {
         }
     }
 
+    /// Drops the contained value in place.
+    ///
+    /// If you have ownership of the `MaybeUninit`, you can use [`assume_init`] instead.
+    ///
+    /// # Safety
+    ///
+    /// It is up to the caller to guarantee that the `MaybeUninit<T>` really is
+    /// in an initialized state. Calling this when the content is not yet fully
+    /// initialized causes undefined behavior.
+    ///
+    /// On top of that, all additional invariants of the type `T` must be
+    /// satisfied, as the `Drop` implementation of `T` (or its members) may
+    /// rely on this. For example, a `1`-initialized [`Vec<T>`] is considered
+    /// initialized (under the current implementation; this does not constitute
+    /// a stable guarantee) because the only requirement the compiler knows
+    /// about it is that the data pointer must be non-null. Dropping such a
+    /// `Vec<T>` however will cause undefined behaviour.
+    ///
+    /// [`assume_init`]: MaybeUninit::assume_init
+    /// [`Vec<T>`]: ../../std/vec/struct.Vec.html
+    #[unstable(feature = "maybe_uninit_extra", issue = "63567")]
+    pub unsafe fn assume_init_drop(&mut self) {
+        // SAFETY: the caller must guarantee that `self` is initialized and
+        // satisfies all invariants of `T`.
+        // Dropping the value in place is safe if that is the case.
+        unsafe { ptr::drop_in_place(self.as_mut_ptr()) }
+    }
+
     /// Gets a shared reference to the contained value.
     ///
     /// This can be useful when we want to access a `MaybeUninit` that has been
index ce3a17a0d28e4d44d33805a0304247debd0e3552..4c027b23584bf7cd6c2102fbe5c3c0f2ceefce96 100644 (file)
@@ -996,9 +996,9 @@ pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> {
     /// Returns an iterator over `N` elements of the slice at a time, starting at the
     /// beginning of the slice.
     ///
-    /// The chunks are slices and do not overlap. If `N` does not divide the length of the
-    /// slice, then the last up to `N-1` elements will be omitted and can be retrieved
-    /// from the `remainder` function of the iterator.
+    /// The chunks are array references and do not overlap. If `N` does not divide the
+    /// length of the slice, then the last up to `N-1` elements will be omitted and can be
+    /// retrieved from the `remainder` function of the iterator.
     ///
     /// This method is the const generic equivalent of [`chunks_exact`].
     ///
@@ -1032,6 +1032,49 @@ pub fn array_chunks<const N: usize>(&self) -> ArrayChunks<'_, T, N> {
         ArrayChunks { iter: array_slice.iter(), rem: snd }
     }
 
+    /// Returns an iterator over `N` elements of the slice at a time, starting at the
+    /// beginning of the slice.
+    ///
+    /// The chunks are mutable array references and do not overlap. If `N` does not divide
+    /// the length of the slice, then the last up to `N-1` elements will be omitted and
+    /// can be retrieved from the `into_remainder` function of the iterator.
+    ///
+    /// This method is the const generic equivalent of [`chunks_exact_mut`].
+    ///
+    /// # Panics
+    ///
+    /// Panics if `N` is 0. This check will most probably get changed to a compile time
+    /// error before this method gets stabilized.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(array_chunks)]
+    /// let v = &mut [0, 0, 0, 0, 0];
+    /// let mut count = 1;
+    ///
+    /// for chunk in v.array_chunks_mut() {
+    ///     *chunk = [count; 2];
+    ///     count += 1;
+    /// }
+    /// assert_eq!(v, &[1, 1, 2, 2, 0]);
+    /// ```
+    ///
+    /// [`chunks_exact_mut`]: #method.chunks_exact_mut
+    #[unstable(feature = "array_chunks", issue = "74985")]
+    #[inline]
+    pub fn array_chunks_mut<const N: usize>(&mut self) -> ArrayChunksMut<'_, T, N> {
+        assert_ne!(N, 0);
+        let len = self.len() / N;
+        let (fst, snd) = self.split_at_mut(len * N);
+        // SAFETY: We cast a slice of `len * N` elements into
+        // a slice of `len` many `N` elements chunks.
+        unsafe {
+            let array_slice: &mut [[T; N]] = from_raw_parts_mut(fst.as_mut_ptr().cast(), len);
+            ArrayChunksMut { iter: array_slice.iter_mut(), rem: snd }
+        }
+    }
+
     /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end
     /// of the slice.
     ///
@@ -5826,7 +5869,7 @@ fn may_have_side_effect() -> bool {
 /// time), starting at the beginning of the slice.
 ///
 /// When the slice len is not evenly divided by the chunk size, the last
-/// up to `chunk_size-1` elements will be omitted but can be retrieved from
+/// up to `N-1` elements will be omitted but can be retrieved from
 /// the [`remainder`] function from the iterator.
 ///
 /// This struct is created by the [`array_chunks`] method on [slices].
@@ -5843,7 +5886,7 @@ pub struct ArrayChunks<'a, T: 'a, const N: usize> {
 
 impl<'a, T, const N: usize> ArrayChunks<'a, T, N> {
     /// Returns the remainder of the original slice that is not going to be
-    /// returned by the iterator. The returned slice has at most `chunk_size-1`
+    /// returned by the iterator. The returned slice has at most `N-1`
     /// elements.
     #[unstable(feature = "array_chunks", issue = "74985")]
     pub fn remainder(&self) -> &'a [T] {
@@ -5929,6 +5972,105 @@ fn may_have_side_effect() -> bool {
     }
 }
 
+/// An iterator over a slice in (non-overlapping) mutable chunks (`N` elements
+/// at a time), starting at the beginning of the slice.
+///
+/// When the slice len is not evenly divided by the chunk size, the last
+/// up to `N-1` elements will be omitted but can be retrieved from
+/// the [`into_remainder`] function from the iterator.
+///
+/// This struct is created by the [`array_chunks_mut`] method on [slices].
+///
+/// [`array_chunks_mut`]: ../../std/primitive.slice.html#method.array_chunks_mut
+/// [`into_remainder`]: ../../std/slice/struct.ArrayChunksMut.html#method.into_remainder
+/// [slices]: ../../std/primitive.slice.html
+#[derive(Debug)]
+#[unstable(feature = "array_chunks", issue = "74985")]
+pub struct ArrayChunksMut<'a, T: 'a, const N: usize> {
+    iter: IterMut<'a, [T; N]>,
+    rem: &'a mut [T],
+}
+
+impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> {
+    /// Returns the remainder of the original slice that is not going to be
+    /// returned by the iterator. The returned slice has at most `N-1`
+    /// elements.
+    #[unstable(feature = "array_chunks", issue = "74985")]
+    pub fn into_remainder(self) -> &'a mut [T] {
+        self.rem
+    }
+}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<'a, T, const N: usize> Iterator for ArrayChunksMut<'a, T, N> {
+    type Item = &'a mut [T; N];
+
+    #[inline]
+    fn next(&mut self) -> Option<&'a mut [T; N]> {
+        self.iter.next()
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+
+    #[inline]
+    fn count(self) -> usize {
+        self.iter.count()
+    }
+
+    #[inline]
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        self.iter.nth(n)
+    }
+
+    #[inline]
+    fn last(self) -> Option<Self::Item> {
+        self.iter.last()
+    }
+
+    unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T; N] {
+        // SAFETY: The safety guarantees of `get_unchecked` are transferred to
+        // the caller.
+        unsafe { self.iter.get_unchecked(i) }
+    }
+}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunksMut<'a, T, N> {
+    #[inline]
+    fn next_back(&mut self) -> Option<&'a mut [T; N]> {
+        self.iter.next_back()
+    }
+
+    #[inline]
+    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
+        self.iter.nth_back(n)
+    }
+}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<T, const N: usize> ExactSizeIterator for ArrayChunksMut<'_, T, N> {
+    fn is_empty(&self) -> bool {
+        self.iter.is_empty()
+    }
+}
+
+#[unstable(feature = "trusted_len", issue = "37572")]
+unsafe impl<T, const N: usize> TrustedLen for ArrayChunksMut<'_, T, N> {}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<T, const N: usize> FusedIterator for ArrayChunksMut<'_, T, N> {}
+
+#[doc(hidden)]
+#[unstable(feature = "array_chunks", issue = "74985")]
+unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunksMut<'a, T, N> {
+    fn may_have_side_effect() -> bool {
+        false
+    }
+}
+
 /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
 /// time), starting at the end of the slice.
 ///
index d4e733eb2bcf52668466933ba6555b92d77a29cb..e221aaf3fd6d65d545c9a40696060bcbd9d99bd0 100644 (file)
@@ -5,7 +5,6 @@
 /// # Examples
 ///
 /// ```
-/// #![feature(future_readiness_fns)]
 /// #![feature(ready_macro)]
 ///
 /// use core::task::{ready, Context, Poll};
@@ -27,7 +26,6 @@
 /// The `ready!` call expands to:
 ///
 /// ```
-/// # #![feature(future_readiness_fns)]
 /// # #![feature(ready_macro)]
 /// #
 /// # use core::task::{Context, Poll};
index 5741f8a53b522b96044120af538f41f58b4bfd12..f39781788d7c04e09a5da36744a9ab77e5b37553 100644 (file)
@@ -108,6 +108,34 @@ impl Duration {
     #[unstable(feature = "duration_constants", issue = "57391")]
     pub const NANOSECOND: Duration = Duration::from_nanos(1);
 
+    /// The minimum duration.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::MIN, Duration::new(0, 0));
+    /// ```
+    #[unstable(feature = "duration_constants", issue = "57391")]
+    pub const MIN: Duration = Duration::from_nanos(0);
+
+    /// The maximum duration.
+    ///
+    /// It is roughly equal to a duration of 584,942,417,355 years.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1));
+    /// ```
+    #[unstable(feature = "duration_constants", issue = "57391")]
+    pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
+
     /// Creates a new `Duration` from the specified number of whole seconds and
     /// additional nanoseconds.
     ///
@@ -450,6 +478,29 @@ pub const fn checked_add(self, rhs: Duration) -> Option<Duration> {
         }
     }
 
+    /// Saturating `Duration` addition. Computes `self + other`, returning [`Duration::MAX`]
+    /// if overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_saturating_ops)]
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
+    /// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
+    /// ```
+    #[unstable(feature = "duration_saturating_ops", issue = "76416")]
+    #[inline]
+    #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
+    pub const fn saturating_add(self, rhs: Duration) -> Duration {
+        match self.checked_add(rhs) {
+            Some(res) => res,
+            None => Duration::MAX,
+        }
+    }
+
     /// Checked `Duration` subtraction. Computes `self - other`, returning [`None`]
     /// if the result would be negative or if overflow occurred.
     ///
@@ -485,6 +536,29 @@ pub const fn checked_sub(self, rhs: Duration) -> Option<Duration> {
         }
     }
 
+    /// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`]
+    /// if the result would be negative or if overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_saturating_ops)]
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1));
+    /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::MIN);
+    /// ```
+    #[unstable(feature = "duration_saturating_ops", issue = "76416")]
+    #[inline]
+    #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
+    pub const fn saturating_sub(self, rhs: Duration) -> Duration {
+        match self.checked_sub(rhs) {
+            Some(res) => res,
+            None => Duration::MIN,
+        }
+    }
+
     /// Checked `Duration` multiplication. Computes `self * other`, returning
     /// [`None`] if overflow occurred.
     ///
@@ -515,6 +589,29 @@ pub const fn checked_mul(self, rhs: u32) -> Option<Duration> {
         None
     }
 
+    /// Saturating `Duration` multiplication. Computes `self * other`, returning
+    /// [`Duration::MAX`] if overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_saturating_ops)]
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2));
+    /// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
+    /// ```
+    #[unstable(feature = "duration_saturating_ops", issue = "76416")]
+    #[inline]
+    #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
+    pub const fn saturating_mul(self, rhs: u32) -> Duration {
+        match self.checked_mul(rhs) {
+            Some(res) => res,
+            None => Duration::MAX,
+        }
+    }
+
     /// Checked `Duration` division. Computes `self / other`, returning [`None`]
     /// if `other == 0`.
     ///
index 4a651e5aa0ee397bdc7974690cc274c3f93373f6..a2e294ace1860f2226350fb80c1df9fd2c647e82 100644 (file)
@@ -10,6 +10,8 @@
 #![feature(core_private_diy_float)]
 #![feature(debug_non_exhaustive)]
 #![feature(dec2flt)]
+#![feature(duration_constants)]
+#![feature(duration_saturating_ops)]
 #![feature(exact_size_is_empty)]
 #![feature(fixed_size_array)]
 #![feature(flt2dec)]
index 743df68699c281516ce455034a66b0c508d9348d..9b31e532a6a9fc2f6d8f41d7f070faf0757c2463 100644 (file)
@@ -564,6 +564,99 @@ fn test_array_chunks_zip() {
     assert_eq!(res, vec![14, 22]);
 }
 
+#[test]
+fn test_array_chunks_mut_infer() {
+    let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
+    for a in v.array_chunks_mut() {
+        let sum = a.iter().sum::<i32>();
+        *a = [sum; 3];
+    }
+    assert_eq!(v, &[3, 3, 3, 12, 12, 12, 6]);
+
+    let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
+    v2.array_chunks_mut().for_each(|[a, b]| core::mem::swap(a, b));
+    assert_eq!(v2, &[1, 0, 3, 2, 5, 4, 6]);
+}
+
+#[test]
+fn test_array_chunks_mut_count() {
+    let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+    let c = v.array_chunks_mut::<3>();
+    assert_eq!(c.count(), 2);
+
+    let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let c2 = v2.array_chunks_mut::<2>();
+    assert_eq!(c2.count(), 2);
+
+    let v3: &mut [i32] = &mut [];
+    let c3 = v3.array_chunks_mut::<2>();
+    assert_eq!(c3.count(), 0);
+}
+
+#[test]
+fn test_array_chunks_mut_nth() {
+    let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+    let mut c = v.array_chunks_mut::<2>();
+    assert_eq!(c.nth(1).unwrap(), &[2, 3]);
+    assert_eq!(c.next().unwrap(), &[4, 5]);
+
+    let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
+    let mut c2 = v2.array_chunks_mut::<3>();
+    assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
+    assert_eq!(c2.next(), None);
+}
+
+#[test]
+fn test_array_chunks_mut_nth_back() {
+    let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+    let mut c = v.array_chunks_mut::<2>();
+    assert_eq!(c.nth_back(1).unwrap(), &[2, 3]);
+    assert_eq!(c.next().unwrap(), &[0, 1]);
+    assert_eq!(c.next(), None);
+
+    let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let mut c2 = v2.array_chunks_mut::<3>();
+    assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]);
+    assert_eq!(c2.next(), None);
+    assert_eq!(c2.next_back(), None);
+
+    let v3: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let mut c3 = v3.array_chunks_mut::<10>();
+    assert_eq!(c3.nth_back(0), None);
+}
+
+#[test]
+fn test_array_chunks_mut_last() {
+    let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+    let c = v.array_chunks_mut::<2>();
+    assert_eq!(c.last().unwrap(), &[4, 5]);
+
+    let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let c2 = v2.array_chunks_mut::<2>();
+    assert_eq!(c2.last().unwrap(), &[2, 3]);
+}
+
+#[test]
+fn test_array_chunks_mut_remainder() {
+    let v: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let c = v.array_chunks_mut::<2>();
+    assert_eq!(c.into_remainder(), &[4]);
+}
+
+#[test]
+fn test_array_chunks_mut_zip() {
+    let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let v2: &[i32] = &[6, 7, 8, 9, 10];
+
+    for (a, b) in v1.array_chunks_mut::<2>().zip(v2.array_chunks::<2>()) {
+        let sum = b.iter().sum::<i32>();
+        for v in a {
+            *v += sum;
+        }
+    }
+    assert_eq!(v1, [13, 14, 19, 20, 4]);
+}
+
 #[test]
 fn test_rchunks_count() {
     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
index 7a6675dc82fa6ef70e9f526c206c100cb95f9ee4..4f90eb63b0472393c2d8e7beeaef66b9f56d0cdb 100644 (file)
@@ -89,6 +89,16 @@ fn checked_add() {
     assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None);
 }
 
+#[test]
+fn saturating_add() {
+    assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
+    assert_eq!(
+        Duration::new(0, 500_000_000).saturating_add(Duration::new(0, 500_000_001)),
+        Duration::new(1, 1)
+    );
+    assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
+}
+
 #[test]
 fn sub() {
     assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), Duration::new(0, 1));
@@ -107,6 +117,17 @@ fn checked_sub() {
     assert_eq!(zero.checked_sub(one_sec), None);
 }
 
+#[test]
+fn saturating_sub() {
+    let zero = Duration::new(0, 0);
+    let one_nano = Duration::new(0, 1);
+    let one_sec = Duration::new(1, 0);
+    assert_eq!(one_nano.saturating_sub(zero), Duration::new(0, 1));
+    assert_eq!(one_sec.saturating_sub(one_nano), Duration::new(0, 999_999_999));
+    assert_eq!(zero.saturating_sub(one_nano), Duration::MIN);
+    assert_eq!(zero.saturating_sub(one_sec), Duration::MIN);
+}
+
 #[test]
 #[should_panic]
 fn sub_bad1() {
@@ -136,6 +157,15 @@ fn checked_mul() {
     assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None);
 }
 
+#[test]
+fn saturating_mul() {
+    assert_eq!(Duration::new(0, 1).saturating_mul(2), Duration::new(0, 2));
+    assert_eq!(Duration::new(1, 1).saturating_mul(3), Duration::new(3, 3));
+    assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4), Duration::new(2, 4));
+    assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4000), Duration::new(2000, 4000));
+    assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
+}
+
 #[test]
 fn div() {
     assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));
index 89dd9fb9b2cd56f8d19c5187d49a1f9aaf1e15ee..5ff74e557547c70330e350ed5b87c10702419391 100644 (file)
@@ -9,7 +9,7 @@
 pub use core::future::{from_generator, get_context, ResumeTy};
 
 #[doc(inline)]
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
 pub use core::future::{pending, ready, Pending, Ready};
 
 #[doc(inline)]
index dc57c1c1f44db7198be286c6348fc3e65dc62766..307e222f713b7cbdee1ab746e0df5aba82807a35 100644 (file)
 #![feature(external_doc)]
 #![feature(fn_traits)]
 #![feature(format_args_nl)]
-#![feature(future_readiness_fns)]
 #![feature(gen_future)]
 #![feature(generator_trait)]
 #![feature(global_asm)]
index 14719a9be5e8a24be91b93c1d54a02de432e6b2d..ff23c3d67e3b4749711572281bc128513f85126d 100644 (file)
@@ -1,3 +1,5 @@
+//! Linux-specific extensions to primitives in the `std::fs` module.
+
 #![stable(feature = "metadata_ext", since = "1.1.0")]
 
 use crate::fs::Metadata;
index d35307162cc7e3b961eeb3d8810f961ba078485c..f179a524336fc1ecd6b1a2e8f14dd519af9628f5 100644 (file)
@@ -1,4 +1,4 @@
-//! Linux-specific definitions
+//! Linux-specific definitions.
 
 #![stable(feature = "raw_ext", since = "1.1.0")]
 
index 1950ffcb21b6755187362b71568a692b26d8674f..a007fd2b6be04c4b879756b7c9f4b49f2ade3f32 100644 (file)
@@ -1,4 +1,4 @@
-//! Linux-specific raw type definitions
+//! Linux-specific raw type definitions.
 
 #![stable(feature = "raw_ext", since = "1.1.0")]
 #![rustc_deprecated(
index 76b34a6b5d84aa64a0a9a429e56218fc59931752..123f85deaf9e3f4320bde10bd6436bb4f900d653 100644 (file)
@@ -1,4 +1,4 @@
-//! Unix-specific extension to the primitives in the `std::ffi` module
+//! Unix-specific extension to the primitives in the `std::ffi` module.
 //!
 //! # Examples
 //!
index 5077e2e28d18b7ccb4ec96f3851119fd4abf8c69..ec7a32b675c02fa50cbb5723214bf44b56ed6022 100644 (file)
@@ -1,4 +1,4 @@
-//! Unix-specific extensions to general I/O primitives
+//! Unix-specific extensions to general I/O primitives.
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
index 0e07106f5ce5cdbdd7d80e3c82acecdea8706d71..320378e30ccb10524303a8225b8df87924770c85 100644 (file)
@@ -1,6 +1,6 @@
-#![stable(feature = "unix_socket", since = "1.10.0")]
+//! Unix-specific networking functionality.
 
-//! Unix-specific networking functionality
+#![stable(feature = "unix_socket", since = "1.10.0")]
 
 #[cfg(all(test, not(target_os = "emscripten")))]
 mod tests;
@@ -594,6 +594,32 @@ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
     pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
         self.0.shutdown(how)
     }
+
+    /// Receives data on the socket from the remote address to which it is
+    /// connected, without removing that data from the queue. On success,
+    /// returns the number of bytes peeked.
+    ///
+    /// Successive calls return the same data. This is accomplished by passing
+    /// `MSG_PEEK` as a flag to the underlying `recv` system call.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_peek)]
+    ///
+    /// use std::os::unix::net::UnixStream;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let socket = UnixStream::connect("/tmp/sock")?;
+    ///     let mut buf = [0; 10];
+    ///     let len = socket.peek(&mut buf).expect("peek failed");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "unix_socket_peek", issue = "none")]
+    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.0.peek(buf)
+    }
 }
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
@@ -1291,6 +1317,33 @@ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
     }
 
+    fn recv_from_flags(
+        &self,
+        buf: &mut [u8],
+        flags: libc::c_int,
+    ) -> io::Result<(usize, SocketAddr)> {
+        let mut count = 0;
+        let addr = SocketAddr::new(|addr, len| unsafe {
+            count = libc::recvfrom(
+                *self.0.as_inner(),
+                buf.as_mut_ptr() as *mut _,
+                buf.len(),
+                flags,
+                addr,
+                len,
+            );
+            if count > 0 {
+                1
+            } else if count == 0 {
+                0
+            } else {
+                -1
+            }
+        })?;
+
+        Ok((count as usize, addr))
+    }
+
     /// Receives data from the socket.
     ///
     /// On success, returns the number of bytes read and the address from
@@ -1311,26 +1364,7 @@ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
     /// ```
     #[stable(feature = "unix_socket", since = "1.10.0")]
     pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        let mut count = 0;
-        let addr = SocketAddr::new(|addr, len| unsafe {
-            count = libc::recvfrom(
-                *self.0.as_inner(),
-                buf.as_mut_ptr() as *mut _,
-                buf.len(),
-                0,
-                addr,
-                len,
-            );
-            if count > 0 {
-                1
-            } else if count == 0 {
-                0
-            } else {
-                -1
-            }
-        })?;
-
-        Ok((count as usize, addr))
+        self.recv_from_flags(buf, 0)
     }
 
     /// Receives data from the socket.
@@ -1601,6 +1635,64 @@ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
     pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
         self.0.shutdown(how)
     }
+
+    /// Receives data on the socket from the remote address to which it is
+    /// connected, without removing that data from the queue. On success,
+    /// returns the number of bytes peeked.
+    ///
+    /// Successive calls return the same data. This is accomplished by passing
+    /// `MSG_PEEK` as a flag to the underlying `recv` system call.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_peek)]
+    ///
+    /// use std::os::unix::net::UnixDatagram;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let socket = UnixDatagram::bind("/tmp/sock")?;
+    ///     let mut buf = [0; 10];
+    ///     let len = socket.peek(&mut buf).expect("peek failed");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "unix_socket_peek", issue = "none")]
+    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.0.peek(buf)
+    }
+
+    /// Receives a single datagram message on the socket, without removing it from the
+    /// queue. On success, returns the number of bytes read and the origin.
+    ///
+    /// The function must be called with valid byte array `buf` of sufficient size to
+    /// hold the message bytes. If a message is too long to fit in the supplied buffer,
+    /// excess bytes may be discarded.
+    ///
+    /// Successive calls return the same data. This is accomplished by passing
+    /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call.
+    ///
+    /// Do not use this function to implement busy waiting, instead use `libc::poll` to
+    /// synchronize IO events on one or more sockets.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_peek)]
+    ///
+    /// use std::os::unix::net::UnixDatagram;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let socket = UnixDatagram::bind("/tmp/sock")?;
+    ///     let mut buf = [0; 10];
+    ///     let (len, addr) = socket.peek_from(&mut buf).expect("peek failed");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "unix_socket_peek", issue = "none")]
+    pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.recv_from_flags(buf, libc::MSG_PEEK)
+    }
 }
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
index be98766f0f3aa3094fb28e1e94d05c8ad1239b16..ee73a6ed538fffb377056a14d2afa151e5ca354c 100644 (file)
@@ -372,3 +372,83 @@ fn test_unix_datagram_timeout_zero_duration() {
 fn abstract_namespace_not_allowed() {
     assert!(UnixStream::connect("\0asdf").is_err());
 }
+
+#[test]
+fn test_unix_stream_peek() {
+    let (txdone, rxdone) = crate::sync::mpsc::channel();
+
+    let dir = tmpdir();
+    let path = dir.path().join("sock");
+
+    let listener = or_panic!(UnixListener::bind(&path));
+    let thread = thread::spawn(move || {
+        let mut stream = or_panic!(listener.accept()).0;
+        or_panic!(stream.write_all(&[1, 3, 3, 7]));
+        or_panic!(rxdone.recv());
+    });
+
+    let mut stream = or_panic!(UnixStream::connect(&path));
+    let mut buf = [0; 10];
+    for _ in 0..2 {
+        assert_eq!(or_panic!(stream.peek(&mut buf)), 4);
+    }
+    assert_eq!(or_panic!(stream.read(&mut buf)), 4);
+
+    or_panic!(stream.set_nonblocking(true));
+    match stream.peek(&mut buf) {
+        Ok(_) => panic!("expected error"),
+        Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
+        Err(e) => panic!("unexpected error: {}", e),
+    }
+
+    or_panic!(txdone.send(()));
+    thread.join().unwrap();
+}
+
+#[test]
+fn test_unix_datagram_peek() {
+    let dir = tmpdir();
+    let path1 = dir.path().join("sock");
+
+    let sock1 = or_panic!(UnixDatagram::bind(&path1));
+    let sock2 = or_panic!(UnixDatagram::unbound());
+    or_panic!(sock2.connect(&path1));
+
+    let msg = b"hello world";
+    or_panic!(sock2.send(msg));
+    for _ in 0..2 {
+        let mut buf = [0; 11];
+        let size = or_panic!(sock1.peek(&mut buf));
+        assert_eq!(size, 11);
+        assert_eq!(msg, &buf[..]);
+    }
+
+    let mut buf = [0; 11];
+    let size = or_panic!(sock1.recv(&mut buf));
+    assert_eq!(size, 11);
+    assert_eq!(msg, &buf[..]);
+}
+
+#[test]
+fn test_unix_datagram_peek_from() {
+    let dir = tmpdir();
+    let path1 = dir.path().join("sock");
+
+    let sock1 = or_panic!(UnixDatagram::bind(&path1));
+    let sock2 = or_panic!(UnixDatagram::unbound());
+    or_panic!(sock2.connect(&path1));
+
+    let msg = b"hello world";
+    or_panic!(sock2.send(msg));
+    for _ in 0..2 {
+        let mut buf = [0; 11];
+        let (size, _) = or_panic!(sock1.peek_from(&mut buf));
+        assert_eq!(size, 11);
+        assert_eq!(msg, &buf[..]);
+    }
+
+    let mut buf = [0; 11];
+    let size = or_panic!(sock1.recv(&mut buf));
+    assert_eq!(size, 11);
+    assert_eq!(msg, &buf[..]);
+}
index 40fa53d484f84885359183d42e5d3ce5b29d9a8c..3199a0bff0bcc57310919ada57e928ee2711cbe2 100644 (file)
@@ -1,4 +1,4 @@
-//! Unix-specific primitives available on all unix platforms
+//! Unix-specific primitives available on all unix platforms.
 
 #![stable(feature = "raw_ext", since = "1.1.0")]
 #![rustc_deprecated(
index 4573ee589321dd488b8d5695b91d9ef9b586331f..e75f9a4bfd5e337ca75e4eb1b031079caa1f5d2a 100644 (file)
@@ -1,3 +1,5 @@
+//! Windows-specific extensions to general I/O primitives.
+
 #![stable(feature = "rust1", since = "1.0.0")]
 
 use crate::fs;
index 7f2a2877828cfe4f3d657a7b69ebab73060ffe45..5014e008eb599c12d911a9b89eaf7d3d42d4388d 100644 (file)
@@ -1,4 +1,4 @@
-//! Windows-specific primitives
+//! Windows-specific primitives.
 
 #![stable(feature = "raw_ext", since = "1.1.0")]
 
index 844a29fadae1e27e788f6a8074a711808bcfedf2..31d4f1f28a86d68ab860b6e2952100af5889cf64 100644 (file)
@@ -87,11 +87,16 @@ fn make_run(_run: RunConfig<'_>) {
 
 pub struct RunConfig<'a> {
     pub builder: &'a Builder<'a>,
-    pub host: TargetSelection,
     pub target: TargetSelection,
     pub path: PathBuf,
 }
 
+impl RunConfig<'_> {
+    pub fn build_triple(&self) -> TargetSelection {
+        self.builder.build.build
+    }
+}
+
 struct StepDescription {
     default: bool,
     only_hosts: bool,
@@ -165,7 +170,6 @@ fn maybe_run(&self, builder: &Builder<'_>, pathset: &PathSet) {
                 pathset, self.name, builder.config.exclude
             );
         }
-        let hosts = &builder.hosts;
 
         // Determine the targets participating in this rule.
         let targets = if self.only_hosts {
@@ -178,16 +182,9 @@ fn maybe_run(&self, builder: &Builder<'_>, pathset: &PathSet) {
             &builder.targets
         };
 
-        for host in hosts {
-            for target in targets {
-                let run = RunConfig {
-                    builder,
-                    path: pathset.path(builder),
-                    host: *host,
-                    target: *target,
-                };
-                (self.make_run)(run);
-            }
+        for target in targets {
+            let run = RunConfig { builder, path: pathset.path(builder), target: *target };
+            (self.make_run)(run);
         }
     }
 
index aeb0d713ef05134f78208b5e48ca3efa1a3b4a5e..c6eac95c34507bcb8954be882db0060df40a2439 100644 (file)
@@ -217,6 +217,16 @@ fn dist_with_hosts() {
                 dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
             ]
         );
+        assert_eq!(
+            first(builder.cache.all::<compile::Std>()),
+            &[
+                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+            ],
+        );
         assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
     }
 
@@ -384,12 +394,9 @@ fn build_all() {
                 compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
                 compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
-                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
                 compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
-                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
             ]
         );
         assert!(!builder.cache.all::<compile::Assemble>().is_empty());
@@ -399,10 +406,8 @@ fn build_all() {
                 compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
                 compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a },
-                compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: a },
                 compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
                 compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b },
-                compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: b },
             ]
         );
     }
@@ -425,12 +430,9 @@ fn build_with_target_flag() {
                 compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
                 compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
                 compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
-                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
                 compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
-                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
             ]
         );
         assert_eq!(
@@ -439,7 +441,6 @@ fn build_with_target_flag() {
                 compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
                 compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
                 compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } },
-                compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } },
             ]
         );
         assert_eq!(
@@ -447,7 +448,6 @@ fn build_with_target_flag() {
             &[
                 compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
                 compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
-                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
             ]
         );
     }
index 9d314e8452b9c58385336f707b7625f83242c2b7..e0dddda83b98feefb9972f748f8cd314b59c5c29 100644 (file)
@@ -45,7 +45,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Std {
-            compiler: run.builder.compiler(run.builder.top_stage, run.host),
+            compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
             target: run.target,
         });
     }
@@ -385,7 +385,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(StartupObjects {
-            compiler: run.builder.compiler(run.builder.top_stage, run.host),
+            compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
             target: run.target,
         });
     }
@@ -454,7 +454,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Rustc {
-            compiler: run.builder.compiler(run.builder.top_stage, run.host),
+            compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
             target: run.target,
         });
     }
index ad2f48778674f192bda9b4c5f797578b852aa758..5a79d3db5c90536b73f1ed9f620bfd8ba0edde62 100644 (file)
@@ -273,10 +273,8 @@ struct TomlConfig {
 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
 struct Build {
     build: Option<String>,
-    #[serde(default)]
-    host: Vec<String>,
-    #[serde(default)]
-    target: Vec<String>,
+    host: Option<Vec<String>>,
+    target: Option<Vec<String>>,
     // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable
     build_dir: Option<String>,
     cargo: Option<String>,
@@ -505,11 +503,6 @@ pub fn parse(args: &[String]) -> Config {
             config.out = dir;
         }
 
-        // If --target was specified but --host wasn't specified, don't run any host-only tests.
-        let has_hosts = !flags.host.is_empty();
-        let has_targets = !flags.target.is_empty();
-        config.skip_only_host_steps = !has_hosts && has_targets;
-
         let toml = file
             .map(|file| {
                 let contents = t!(fs::read_to_string(&file));
@@ -528,25 +521,28 @@ pub fn parse(args: &[String]) -> Config {
             .unwrap_or_else(TomlConfig::default);
 
         let build = toml.build.clone().unwrap_or_default();
-        // set by bootstrap.py
-        config.hosts.push(config.build);
-        for host in build.host.iter().map(|h| TargetSelection::from_user(h)) {
-            if !config.hosts.contains(&host) {
-                config.hosts.push(host);
-            }
-        }
-        for target in config
-            .hosts
-            .iter()
-            .copied()
-            .chain(build.target.iter().map(|h| TargetSelection::from_user(h)))
-        {
-            if !config.targets.contains(&target) {
-                config.targets.push(target);
-            }
-        }
-        config.hosts = if !flags.host.is_empty() { flags.host } else { config.hosts };
-        config.targets = if !flags.target.is_empty() { flags.target } else { config.targets };
+
+        // If --target was specified but --host wasn't specified, don't run any host-only tests.
+        let has_hosts = build.host.is_some() || flags.host.is_some();
+        let has_targets = build.target.is_some() || flags.target.is_some();
+        config.skip_only_host_steps = !has_hosts && has_targets;
+
+        config.hosts = if let Some(arg_host) = flags.host.clone() {
+            arg_host
+        } else if let Some(file_host) = build.host {
+            file_host.iter().map(|h| TargetSelection::from_user(h)).collect()
+        } else {
+            vec![config.build]
+        };
+        config.targets = if let Some(arg_target) = flags.target.clone() {
+            arg_target
+        } else if let Some(file_target) = build.target {
+            file_target.iter().map(|h| TargetSelection::from_user(h)).collect()
+        } else {
+            // If target is *not* configured, then default to the host
+            // toolchains.
+            config.hosts.clone()
+        };
 
         config.nodejs = build.nodejs.map(PathBuf::from);
         config.gdb = build.gdb.map(PathBuf::from);
index 797a1ce20b48bbb47412c7eb1a490b9af907745b..e22cdb13928364e7d6ee5882a5a6e6b0bd4b706b 100644 (file)
@@ -605,7 +605,9 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(DebuggerScripts {
-            sysroot: run.builder.sysroot(run.builder.compiler(run.builder.top_stage, run.host)),
+            sysroot: run
+                .builder
+                .sysroot(run.builder.compiler(run.builder.top_stage, run.build_triple())),
             host: run.target,
         });
     }
@@ -2524,6 +2526,14 @@ fn make_run(run: RunConfig<'_>) {
     fn run(self, builder: &Builder<'_>) -> Option<PathBuf> {
         let target = self.target;
 
+        /* run only if llvm-config isn't used */
+        if let Some(config) = builder.config.target_config.get(&target) {
+            if let Some(ref _s) = config.llvm_config {
+                builder.info(&format!("Skipping RustDev ({}): external LLVM", target));
+                return None;
+            }
+        }
+
         builder.info(&format!("Dist RustDev ({})", target));
         let _time = timeit(builder);
         let src = builder.src.join("src/llvm-project/llvm");
@@ -2536,6 +2546,7 @@ fn run(self, builder: &Builder<'_>) -> Option<PathBuf> {
         // Prepare the image directory
         let dst_bindir = image.join("bin");
         t!(fs::create_dir_all(&dst_bindir));
+
         let exe = builder.llvm_out(target).join("bin").join(exe("llvm-config", target));
         builder.install(&exe, &dst_bindir, 0o755);
         builder.install(&builder.llvm_filecheck(target), &dst_bindir, 0o755);
index 2db4bb07a9ff1646486389a8fef6a8f7150a9fa0..ff8468574469eefaa7deadb3fe19d79bd4be3aae 100644 (file)
@@ -20,8 +20,8 @@ pub struct Flags {
     pub stage: Option<u32>,
     pub keep_stage: Vec<u32>,
 
-    pub host: Vec<TargetSelection>,
-    pub target: Vec<TargetSelection>,
+    pub host: Option<Vec<TargetSelection>>,
+    pub target: Option<Vec<TargetSelection>>,
     pub config: Option<PathBuf>,
     pub jobs: Option<u32>,
     pub cmd: Subcommand,
@@ -526,14 +526,26 @@ pub fn parse(args: &[String]) -> Flags {
                 .into_iter()
                 .map(|j| j.parse().expect("`keep-stage` should be a number"))
                 .collect(),
-            host: split(&matches.opt_strs("host"))
-                .into_iter()
-                .map(|x| TargetSelection::from_user(&x))
-                .collect::<Vec<_>>(),
-            target: split(&matches.opt_strs("target"))
-                .into_iter()
-                .map(|x| TargetSelection::from_user(&x))
-                .collect::<Vec<_>>(),
+            host: if matches.opt_present("host") {
+                Some(
+                    split(&matches.opt_strs("host"))
+                        .into_iter()
+                        .map(|x| TargetSelection::from_user(&x))
+                        .collect::<Vec<_>>(),
+                )
+            } else {
+                None
+            },
+            target: if matches.opt_present("target") {
+                Some(
+                    split(&matches.opt_strs("target"))
+                        .into_iter()
+                        .map(|x| TargetSelection::from_user(&x))
+                        .collect::<Vec<_>>(),
+                )
+            } else {
+                None
+            },
             config: cfg_file,
             jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
             cmd,
index 732028fb6ed4789d84ebdedeab94b1718c655e10..045dda2d4cb4cbbc5da6631bda9dc4616e9c670f 100644 (file)
@@ -584,7 +584,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let compiler = run.builder.compiler(run.builder.top_stage, run.host);
+        let compiler = run.builder.compiler(run.builder.top_stage, run.target);
 
         run.builder.ensure(RustdocTheme { compiler });
     }
@@ -651,7 +651,6 @@ fn run(self, builder: &Builder<'_>) {
 
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct RustdocJSNotStd {
-    pub host: TargetSelection,
     pub target: TargetSelection,
     pub compiler: Compiler,
 }
@@ -666,8 +665,8 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let compiler = run.builder.compiler(run.builder.top_stage, run.host);
-        run.builder.ensure(RustdocJSNotStd { host: run.host, target: run.target, compiler });
+        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+        run.builder.ensure(RustdocJSNotStd { target: run.target, compiler });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -688,7 +687,6 @@ fn run(self, builder: &Builder<'_>) {
 
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct RustdocUi {
-    pub host: TargetSelection,
     pub target: TargetSelection,
     pub compiler: Compiler,
 }
@@ -703,8 +701,8 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let compiler = run.builder.compiler(run.builder.top_stage, run.host);
-        run.builder.ensure(RustdocUi { host: run.host, target: run.target, compiler });
+        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+        run.builder.ensure(RustdocUi { target: run.target, compiler });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -873,7 +871,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
             }
 
             fn make_run(run: RunConfig<'_>) {
-                let compiler = run.builder.compiler(run.builder.top_stage, run.host);
+                let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
 
                 run.builder.ensure($name { compiler, target: run.target });
             }
@@ -1422,7 +1420,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
                 fn make_run(run: RunConfig<'_>) {
                     run.builder.ensure($name {
-                        compiler: run.builder.compiler(run.builder.top_stage, run.host),
+                        compiler: run.builder.compiler(run.builder.top_stage, run.target),
                     });
                 }
 
@@ -1469,7 +1467,7 @@ fn make_run(run: RunConfig<'_>) {
         // error_index_generator depends on librustdoc. Use the compiler that
         // is normally used to build rustdoc for other tests (like compiletest
         // tests in src/test/rustdoc) so that it shares the same artifacts.
-        let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host);
+        let compiler = run.builder.compiler_for(run.builder.top_stage, run.target, run.target);
         run.builder.ensure(ErrorIndex { compiler });
     }
 
@@ -1573,7 +1571,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
-        let compiler = builder.compiler(builder.top_stage, run.host);
+        let compiler = builder.compiler(builder.top_stage, run.build_triple());
 
         for krate in builder.in_tree_crates("rustc-main") {
             if krate.path.ends_with(&run.path) {
@@ -1620,7 +1618,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
-        let compiler = builder.compiler(builder.top_stage, run.host);
+        let compiler = builder.compiler(builder.top_stage, run.build_triple());
 
         let test_kind = builder.kind.into();
 
@@ -1668,7 +1666,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 
     fn make_run(run: RunConfig<'_>) {
         let builder = run.builder;
-        let compiler = builder.compiler(builder.top_stage, run.host);
+        let compiler = builder.compiler(builder.top_stage, run.build_triple());
 
         let make = |mode: Mode, krate: &CargoCrate| {
             let test_kind = builder.kind.into();
@@ -1808,7 +1806,7 @@ fn make_run(run: RunConfig<'_>) {
 
         let test_kind = builder.kind.into();
 
-        builder.ensure(CrateRustdoc { host: run.host, test_kind });
+        builder.ensure(CrateRustdoc { host: run.target, test_kind });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -2054,7 +2052,6 @@ fn make_run(run: RunConfig<'_>) {
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct TierCheck {
     pub compiler: Compiler,
-    target: TargetSelection,
 }
 
 impl Step for TierCheck {
@@ -2067,18 +2064,19 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host);
-        run.builder.ensure(TierCheck { compiler, target: run.host });
+        let compiler =
+            run.builder.compiler_for(run.builder.top_stage, run.builder.build.build, run.target);
+        run.builder.ensure(TierCheck { compiler });
     }
 
     /// Tests the Platform Support page in the rustc book.
     fn run(self, builder: &Builder<'_>) {
-        builder.ensure(compile::Std { compiler: self.compiler, target: self.target });
+        builder.ensure(compile::Std { compiler: self.compiler, target: self.compiler.host });
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             self.compiler,
-            Mode::ToolRustc,
-            self.target,
+            Mode::ToolStd,
+            self.compiler.host,
             "run",
             "src/tools/tier-check",
             SourceType::InTree,
index e8d7de7a5dcaa19e40e093e518591e16ec37d8f6..a607f0fe258d4cf257606ed5c17128abc2ef3a68 100644 (file)
@@ -469,8 +469,9 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder
-            .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.host) });
+        run.builder.ensure(Rustdoc {
+            compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
+        });
     }
 
     fn run(self, builder: &Builder<'_>) -> PathBuf {
index 1bdbad4675556781bfa3a513b7714ad952be1e58..9d784d24609dc5ea294be1cda5fd6c764a1edcda 100644 (file)
@@ -1364,16 +1364,16 @@ fn clean(&self, cx: &DocContext<'_>) -> Type {
             TyKind::Slice(ref ty) => Slice(box ty.clean(cx)),
             TyKind::Array(ref ty, ref length) => {
                 let def_id = cx.tcx.hir().local_def_id(length.hir_id);
-                let length = match cx.tcx.const_eval_poly(def_id.to_def_id()) {
-                    Ok(length) => {
-                        print_const(cx, ty::Const::from_value(cx.tcx, length, cx.tcx.types.usize))
-                    }
-                    Err(_) => cx
-                        .sess()
-                        .source_map()
-                        .span_to_snippet(cx.tcx.def_span(def_id))
-                        .unwrap_or_else(|_| "_".to_string()),
-                };
+                // NOTE(min_const_generics): We can't use `const_eval_poly` for constants
+                // as we currently do not supply the parent generics to anonymous constants
+                // but do allow `ConstKind::Param`.
+                //
+                // `const_eval_poly` tries to to first substitute generic parameters which
+                // results in an ICE while manually constructing the constant and using `eval`
+                // does nothing for `ConstKind::Param`.
+                let ct = ty::Const::from_anon_const(cx.tcx, def_id);
+                let param_env = cx.tcx.param_env(def_id);
+                let length = print_const(cx, ct.eval(cx.tcx, param_env));
                 Array(box ty.clean(cx), length)
             }
             TyKind::Tup(ref tys) => Tuple(tys.clean(cx)),
index 5d10e2e149b32ee005f2954c9bf368b5d007033c..5780610c862102347179867fc2d6afcdaa89f46f 100644 (file)
@@ -17,8 +17,9 @@
 use rustc_span::symbol::Ident;
 use rustc_span::symbol::Symbol;
 use rustc_span::DUMMY_SP;
-use smallvec::SmallVec;
+use smallvec::{smallvec, SmallVec};
 
+use std::borrow::Cow;
 use std::cell::Cell;
 use std::ops::Range;
 
@@ -46,19 +47,73 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
     }
 }
 
-enum ErrorKind {
-    ResolutionFailure,
+enum ErrorKind<'a> {
+    Resolve(Box<ResolutionFailure<'a>>),
     AnchorFailure(AnchorFailure),
 }
 
+impl<'a> From<ResolutionFailure<'a>> for ErrorKind<'a> {
+    fn from(err: ResolutionFailure<'a>) -> Self {
+        ErrorKind::Resolve(box err)
+    }
+}
+
+#[derive(Debug)]
+enum ResolutionFailure<'a> {
+    /// This resolved, but with the wrong namespace.
+    /// `Namespace` is the expected namespace (as opposed to the actual).
+    WrongNamespace(Res, Namespace),
+    /// This has a partial resolution, but is not in the TypeNS and so cannot
+    /// have associated items or fields.
+    CannotHaveAssociatedItems(Res, Namespace),
+    /// `name` is the base name of the path (not necessarily the whole link)
+    NotInScope { module_id: DefId, name: Cow<'a, str> },
+    /// this is a primitive type without an impls (no associated methods)
+    /// when will this actually happen?
+    /// the `Res` is the primitive it resolved to
+    NoPrimitiveImpl(Res, String),
+    /// `[u8::not_found]`
+    /// the `Res` is the primitive it resolved to
+    NoPrimitiveAssocItem { res: Res, prim_name: &'a str, assoc_item: Symbol },
+    /// `[S::not_found]`
+    /// the `String` is the associated item that wasn't found
+    NoAssocItem(Res, Symbol),
+    /// should not ever happen
+    NoParentItem,
+    /// this could be an enum variant, but the last path fragment wasn't resolved.
+    /// the `String` is the variant that didn't exist
+    NotAVariant(Res, Symbol),
+    /// used to communicate that this should be ignored, but shouldn't be reported to the user
+    Dummy,
+}
+
+impl ResolutionFailure<'a> {
+    // A partial or full resolution
+    fn res(&self) -> Option<Res> {
+        use ResolutionFailure::*;
+        match self {
+            NoPrimitiveAssocItem { res, .. }
+            | NoAssocItem(res, _)
+            | NoPrimitiveImpl(res, _)
+            | NotAVariant(res, _)
+            | WrongNamespace(res, _)
+            | CannotHaveAssociatedItems(res, _) => Some(*res),
+            NotInScope { .. } | NoParentItem | Dummy => None,
+        }
+    }
+
+    // This resolved fully (not just partially) but is erroneous for some other reason
+    fn full_res(&self) -> Option<Res> {
+        match self {
+            Self::WrongNamespace(res, _) => Some(*res),
+            _ => None,
+        }
+    }
+}
+
 enum AnchorFailure {
     MultipleAnchors,
-    Primitive,
-    Variant,
-    AssocConstant,
-    AssocType,
-    Field,
-    Method,
+    RustdocAnchorConflict(Res),
 }
 
 struct LinkCollector<'a, 'tcx> {
@@ -68,7 +123,7 @@ struct LinkCollector<'a, 'tcx> {
     /// This is used to store the kind of associated items,
     /// because `clean` and the disambiguator code expect them to be different.
     /// See the code for associated items on inherent impls for details.
-    kind_side_channel: Cell<Option<DefKind>>,
+    kind_side_channel: Cell<Option<(DefKind, DefId)>>,
 }
 
 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
@@ -78,17 +133,25 @@ fn new(cx: &'a DocContext<'tcx>) -> Self {
 
     fn variant_field(
         &self,
-        path_str: &str,
+        path_str: &'path str,
         current_item: &Option<String>,
         module_id: DefId,
-    ) -> Result<(Res, Option<String>), ErrorKind> {
+        extra_fragment: &Option<String>,
+    ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
         let cx = self.cx;
 
+        debug!("looking for enum variant {}", path_str);
         let mut split = path_str.rsplitn(3, "::");
-        let variant_field_name =
-            split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
+        let variant_field_name = split
+            .next()
+            .map(|f| Symbol::intern(f))
+            .expect("fold_item should ensure link is non-empty");
         let variant_name =
-            split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
+            // we're not sure this is a variant at all, so use the full string
+            split.next().map(|f| Symbol::intern(f)).ok_or_else(|| ResolutionFailure::NotInScope {
+                module_id,
+                name: path_str.into(),
+            })?;
         let path = split
             .next()
             .map(|f| {
@@ -99,14 +162,18 @@ fn variant_field(
                 }
                 f.to_owned()
             })
-            .ok_or(ErrorKind::ResolutionFailure)?;
-        let (_, ty_res) = cx
+            .ok_or_else(|| ResolutionFailure::NotInScope {
+                module_id,
+                name: variant_name.to_string().into(),
+            })?;
+        let ty_res = cx
             .enter_resolver(|resolver| {
                 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
             })
-            .map_err(|_| ErrorKind::ResolutionFailure)?;
+            .map(|(_, res)| res)
+            .unwrap_or(Res::Err);
         if let Res::Err = ty_res {
-            return Err(ErrorKind::ResolutionFailure);
+            return Err(ResolutionFailure::NotInScope { module_id, name: path.into() }.into());
         }
         let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
         match ty_res {
@@ -118,7 +185,9 @@ fn variant_field(
                     .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
                     .any(|item| item.ident.name == variant_name)
                 {
-                    return Err(ErrorKind::ResolutionFailure);
+                    // This is just to let `fold_item` know that this shouldn't be considered;
+                    // it's a bug for the error to make it to the user
+                    return Err(ResolutionFailure::Dummy.into());
                 }
                 match cx.tcx.type_of(did).kind() {
                     ty::Adt(def, _) if def.is_enum() => {
@@ -131,18 +200,43 @@ fn variant_field(
                                 )),
                             ))
                         } else {
-                            Err(ErrorKind::ResolutionFailure)
+                            Err(ResolutionFailure::NotAVariant(ty_res, variant_field_name).into())
                         }
                     }
-                    _ => Err(ErrorKind::ResolutionFailure),
+                    _ => unreachable!(),
                 }
             }
-            _ => Err(ErrorKind::ResolutionFailure),
+            // `variant_field` looks at 3 different path segments in a row.
+            // But `NoAssocItem` assumes there are only 2. Check to see if there's
+            // an intermediate segment that resolves.
+            _ => {
+                let intermediate_path = format!("{}::{}", path, variant_name);
+                // NOTE: we have to be careful here, because we're already in `resolve`.
+                // We know this doesn't recurse forever because we use a shorter path each time.
+                // NOTE: this uses `TypeNS` because nothing else has a valid path segment after
+                let kind = if let Some(intermediate) = self.check_full_res(
+                    TypeNS,
+                    &intermediate_path,
+                    Some(module_id),
+                    current_item,
+                    extra_fragment,
+                ) {
+                    ResolutionFailure::NoAssocItem(intermediate, variant_field_name)
+                } else {
+                    // Even with the shorter path, it didn't resolve, so say that.
+                    ResolutionFailure::NoAssocItem(ty_res, variant_name)
+                };
+                Err(kind.into())
+            }
         }
     }
 
     /// Resolves a string as a macro.
-    fn macro_resolve(&self, path_str: &str, parent_id: Option<DefId>) -> Option<Res> {
+    fn macro_resolve(
+        &self,
+        path_str: &'a str,
+        parent_id: Option<DefId>,
+    ) -> Result<Res, ResolutionFailure<'a>> {
         let cx = self.cx;
         let path = ast::Path::from_ident(Ident::from_str(path_str));
         cx.enter_resolver(|resolver| {
@@ -154,11 +248,11 @@ fn macro_resolve(&self, path_str: &str, parent_id: Option<DefId>) -> Option<Res>
                 false,
             ) {
                 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
-                    return Some(res.map_id(|_| panic!("unexpected id")));
+                    return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
                 }
             }
             if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
-                return Some(res.map_id(|_| panic!("unexpected id")));
+                return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
             }
             if let Some(module_id) = parent_id {
                 debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
@@ -168,25 +262,47 @@ fn macro_resolve(&self, path_str: &str, parent_id: Option<DefId>) -> Option<Res>
                     // don't resolve builtins like `#[derive]`
                     if let Res::Def(..) = res {
                         let res = res.map_id(|_| panic!("unexpected node_id"));
-                        return Some(res);
+                        return Some(Ok(res));
                     }
                 }
             } else {
                 debug!("attempting to resolve item without parent module: {}", path_str);
+                return Some(Err(ResolutionFailure::NoParentItem));
             }
             None
         })
+        // This weird control flow is so we don't borrow the resolver more than once at a time
+        .unwrap_or_else(|| {
+            let mut split = path_str.rsplitn(2, "::");
+            if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) {
+                if let Some(res) = self.check_full_res(TypeNS, parent, parent_id, &None, &None) {
+                    return Err(if matches!(res, Res::PrimTy(_)) {
+                        ResolutionFailure::NoPrimitiveAssocItem {
+                            res,
+                            prim_name: parent,
+                            assoc_item: Symbol::intern(base),
+                        }
+                    } else {
+                        ResolutionFailure::NoAssocItem(res, Symbol::intern(base))
+                    });
+                }
+            }
+            Err(ResolutionFailure::NotInScope {
+                module_id: parent_id.expect("already saw `Some` when resolving as a macro"),
+                name: path_str.into(),
+            })
+        })
     }
     /// Resolves a string as a path within a particular namespace. Also returns an optional
     /// URL fragment in the case of variants and methods.
-    fn resolve(
+    fn resolve<'path>(
         &self,
-        path_str: &str,
+        path_str: &'path str,
         ns: Namespace,
         current_item: &Option<String>,
         parent_id: Option<DefId>,
         extra_fragment: &Option<String>,
-    ) -> Result<(Res, Option<String>), ErrorKind> {
+    ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
         let cx = self.cx;
 
         // In case we're in a module, try to resolve the relative path.
@@ -196,8 +312,8 @@ fn resolve(
             });
             debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
             let result = match result {
-                Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure),
-                _ => result.map_err(|_| ErrorKind::ResolutionFailure),
+                Ok((_, Res::Err)) => Err(()),
+                x => x,
             };
 
             if let Ok((_, res)) = result {
@@ -213,7 +329,9 @@ fn resolve(
                     // Not a trait item; just return what we found.
                     Res::PrimTy(..) => {
                         if extra_fragment.is_some() {
-                            return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
+                            return Err(ErrorKind::AnchorFailure(
+                                AnchorFailure::RustdocAnchorConflict(res),
+                            ));
                         }
                         return Ok((res, Some(path_str.to_owned())));
                     }
@@ -226,20 +344,22 @@ fn resolve(
                 };
 
                 if value != (ns == ValueNS) {
-                    return Err(ErrorKind::ResolutionFailure);
+                    return Err(ResolutionFailure::WrongNamespace(res, ns).into());
                 }
             } else if let Some((path, prim)) = is_primitive(path_str, ns) {
                 if extra_fragment.is_some() {
-                    return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
+                    return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(
+                        prim,
+                    )));
                 }
                 return Ok((prim, Some(path.to_owned())));
             }
 
             // Try looking for methods and associated items.
             let mut split = path_str.rsplitn(2, "::");
-            let item_name =
-                split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
-            let path = split
+            // this can be an `unwrap()` because we ensure the link is never empty
+            let item_name = Symbol::intern(split.next().unwrap());
+            let path_root = split
                 .next()
                 .map(|f| {
                     if f == "self" || f == "Self" {
@@ -249,10 +369,17 @@ fn resolve(
                     }
                     f.to_owned()
                 })
-                .ok_or(ErrorKind::ResolutionFailure)?;
-
-            if let Some((path, prim)) = is_primitive(&path, TypeNS) {
-                for &impl_ in primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)? {
+                // If there's no `::`, it's not an associated item.
+                // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
+                .ok_or_else(|| {
+                    debug!("found no `::`, assumming {} was correctly not in scope", item_name);
+                    ResolutionFailure::NotInScope { module_id, name: item_name.to_string().into() }
+                })?;
+
+            if let Some((path, prim)) = is_primitive(&path_root, TypeNS) {
+                let impls = primitive_impl(cx, &path)
+                    .ok_or_else(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?;
+                for &impl_ in impls {
                     let link = cx
                         .tcx
                         .associated_items(impl_)
@@ -272,21 +399,54 @@ fn resolve(
                         return Ok(link);
                     }
                 }
-                return Err(ErrorKind::ResolutionFailure);
+                debug!(
+                    "returning primitive error for {}::{} in {} namespace",
+                    path,
+                    item_name,
+                    ns.descr()
+                );
+                return Err(ResolutionFailure::NoPrimitiveAssocItem {
+                    res: prim,
+                    prim_name: path,
+                    assoc_item: item_name,
+                }
+                .into());
             }
 
-            let (_, ty_res) = cx
+            let ty_res = cx
                 .enter_resolver(|resolver| {
-                    resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
+                    // only types can have associated items
+                    resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id)
                 })
-                .map_err(|_| ErrorKind::ResolutionFailure)?;
-            if let Res::Err = ty_res {
-                return if ns == Namespace::ValueNS {
-                    self.variant_field(path_str, current_item, module_id)
-                } else {
-                    Err(ErrorKind::ResolutionFailure)
-                };
-            }
+                .map(|(_, res)| res);
+            let ty_res = match ty_res {
+                Err(()) | Ok(Res::Err) => {
+                    return if ns == Namespace::ValueNS {
+                        self.variant_field(path_str, current_item, module_id, extra_fragment)
+                    } else {
+                        // See if it only broke because of the namespace.
+                        let kind = cx.enter_resolver(|resolver| {
+                            // NOTE: this doesn't use `check_full_res` because we explicitly want to ignore `TypeNS` (we already checked it)
+                            for &ns in &[MacroNS, ValueNS] {
+                                match resolver
+                                    .resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id)
+                                {
+                                    Ok((_, Res::Err)) | Err(()) => {}
+                                    Ok((_, res)) => {
+                                        let res = res.map_id(|_| panic!("unexpected node_id"));
+                                        return ResolutionFailure::CannotHaveAssociatedItems(
+                                            res, ns,
+                                        );
+                                    }
+                                }
+                            }
+                            ResolutionFailure::NotInScope { module_id, name: path_root.into() }
+                        });
+                        Err(kind.into())
+                    };
+                }
+                Ok(res) => res,
+            };
             let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
             let res = match ty_res {
                 Res::Def(
@@ -295,7 +455,7 @@ fn resolve(
                 ) => {
                     debug!("looking for associated item named {} for item {:?}", item_name, did);
                     // Checks if item_name belongs to `impl SomeItem`
-                    let kind = cx
+                    let assoc_item = cx
                         .tcx
                         .inherent_impls(did)
                         .iter()
@@ -307,7 +467,7 @@ fn resolve(
                                 imp,
                             )
                         })
-                        .map(|item| item.kind)
+                        .map(|item| (item.kind, item.def_id))
                         // There should only ever be one associated item that matches from any inherent impl
                         .next()
                         // Check if item_name belongs to `impl SomeTrait for SomeItem`
@@ -323,26 +483,25 @@ fn resolve(
                             kind
                         });
 
-                    if let Some(kind) = kind {
+                    if let Some((kind, id)) = assoc_item {
                         let out = match kind {
                             ty::AssocKind::Fn => "method",
                             ty::AssocKind::Const => "associatedconstant",
                             ty::AssocKind::Type => "associatedtype",
                         };
                         Some(if extra_fragment.is_some() {
-                            Err(ErrorKind::AnchorFailure(if kind == ty::AssocKind::Fn {
-                                AnchorFailure::Method
-                            } else {
-                                AnchorFailure::AssocConstant
-                            }))
+                            Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(
+                                ty_res,
+                            )))
                         } else {
                             // HACK(jynelson): `clean` expects the type, not the associated item.
                             // but the disambiguator logic expects the associated item.
                             // Store the kind in a side channel so that only the disambiguator logic looks at it.
-                            self.kind_side_channel.set(Some(kind.as_def_kind()));
+                            self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
                             Ok((ty_res, Some(format!("{}.{}", out, item_name))))
                         })
                     } else if ns == Namespace::ValueNS {
+                        debug!("looking for variants or fields named {} for {:?}", item_name, did);
                         match cx.tcx.type_of(did).kind() {
                             ty::Adt(def, _) => {
                                 let field = if def.is_enum() {
@@ -355,11 +514,17 @@ fn resolve(
                                 };
                                 field.map(|item| {
                                     if extra_fragment.is_some() {
-                                        Err(ErrorKind::AnchorFailure(if def.is_enum() {
-                                            AnchorFailure::Variant
-                                        } else {
-                                            AnchorFailure::Field
-                                        }))
+                                        let res = Res::Def(
+                                            if def.is_enum() {
+                                                DefKind::Variant
+                                            } else {
+                                                DefKind::Field
+                                            },
+                                            item.did,
+                                        );
+                                        Err(ErrorKind::AnchorFailure(
+                                            AnchorFailure::RustdocAnchorConflict(res),
+                                        ))
                                     } else {
                                         Ok((
                                             ty_res,
@@ -380,7 +545,7 @@ fn resolve(
                         }
                     } else {
                         // We already know this isn't in ValueNS, so no need to check variant_field
-                        return Err(ErrorKind::ResolutionFailure);
+                        return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into());
                     }
                 }
                 Res::Def(DefKind::Trait, did) => cx
@@ -401,13 +566,9 @@ fn resolve(
                         };
 
                         if extra_fragment.is_some() {
-                            Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Const {
-                                AnchorFailure::AssocConstant
-                            } else if item.kind == ty::AssocKind::Type {
-                                AnchorFailure::AssocType
-                            } else {
-                                AnchorFailure::Method
-                            }))
+                            Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(
+                                ty_res,
+                            )))
                         } else {
                             let res = Res::Def(item.kind.as_def_kind(), item.def_id);
                             Ok((res, Some(format!("{}.{}", kind, item_name))))
@@ -417,14 +578,54 @@ fn resolve(
             };
             res.unwrap_or_else(|| {
                 if ns == Namespace::ValueNS {
-                    self.variant_field(path_str, current_item, module_id)
+                    self.variant_field(path_str, current_item, module_id, extra_fragment)
                 } else {
-                    Err(ErrorKind::ResolutionFailure)
+                    Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into())
                 }
             })
         } else {
             debug!("attempting to resolve item without parent module: {}", path_str);
-            Err(ErrorKind::ResolutionFailure)
+            Err(ResolutionFailure::NoParentItem.into())
+        }
+    }
+
+    /// Used for reporting better errors.
+    ///
+    /// Returns whether the link resolved 'fully' in another namespace.
+    /// 'fully' here means that all parts of the link resolved, not just some path segments.
+    /// This returns the `Res` even if it was erroneous for some reason
+    /// (such as having invalid URL fragments or being in the wrong namespace).
+    fn check_full_res(
+        &self,
+        ns: Namespace,
+        path_str: &str,
+        base_node: Option<DefId>,
+        current_item: &Option<String>,
+        extra_fragment: &Option<String>,
+    ) -> Option<Res> {
+        let check_full_res_inner = |this: &Self, result: Result<Res, ErrorKind<'_>>| {
+            let res = match result {
+                Ok(res) => Some(res),
+                Err(ErrorKind::Resolve(box kind)) => kind.full_res(),
+                Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => {
+                    Some(res)
+                }
+                Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None,
+            };
+            this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res)
+        };
+        // cannot be used for macro namespace
+        let check_full_res = |this: &Self, ns| {
+            let result = this.resolve(path_str, ns, current_item, base_node, extra_fragment);
+            check_full_res_inner(this, result.map(|(res, _)| res))
+        };
+        let check_full_res_macro = |this: &Self| {
+            let result = this.macro_resolve(path_str, base_node);
+            check_full_res_inner(this, result.map_err(ErrorKind::from))
+        };
+        match ns {
+            Namespace::MacroNS => check_full_res_macro(self),
+            Namespace::TypeNS | Namespace::ValueNS => check_full_res(self, ns),
         }
     }
 }
@@ -435,7 +636,7 @@ fn resolve_associated_trait_item(
     item_name: Symbol,
     ns: Namespace,
     cx: &DocContext<'_>,
-) -> Option<ty::AssocKind> {
+) -> Option<(ty::AssocKind, DefId)> {
     let ty = cx.tcx.type_of(did);
     // First consider automatic impls: `impl From<T> for T`
     let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
@@ -463,7 +664,7 @@ fn resolve_associated_trait_item(
                             // but provided methods come directly from `tcx`.
                             // Fortunately, we don't need the whole method, we just need to know
                             // what kind of associated item it is.
-                            Some((assoc.def_id, kind))
+                            Some((kind, assoc.def_id))
                         });
                         let assoc = items.next();
                         debug_assert_eq!(items.count(), 0);
@@ -485,7 +686,7 @@ fn resolve_associated_trait_item(
                                 ns,
                                 trait_,
                             )
-                            .map(|assoc| (assoc.def_id, assoc.kind))
+                            .map(|assoc| (assoc.kind, assoc.def_id))
                     }
                 }
                 _ => panic!("get_impls returned something that wasn't an impl"),
@@ -502,12 +703,12 @@ fn resolve_associated_trait_item(
             cx.tcx
                 .associated_items(trait_)
                 .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
-                .map(|assoc| (assoc.def_id, assoc.kind))
+                .map(|assoc| (assoc.kind, assoc.def_id))
         }));
     }
     // FIXME: warn about ambiguity
     debug!("the candidates were {:?}", candidates);
-    candidates.pop().map(|(_, kind)| kind)
+    candidates.pop()
 }
 
 /// Given a type, return all traits in scope in `module` implemented by that type.
@@ -536,7 +737,7 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx
             let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
             // Check if these are the same type.
             let impl_type = trait_ref.self_ty();
-            debug!(
+            trace!(
                 "comparing type {} with kind {:?} against type {:?}",
                 impl_type,
                 impl_type.kind(),
@@ -562,10 +763,10 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx
 /// Check for resolve collisions between a trait and its derive
 ///
 /// These are common and we should just resolve to the trait in that case
-fn is_derive_trait_collision<T>(ns: &PerNS<Option<(Res, T)>>) -> bool {
+fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_>>>) -> bool {
     if let PerNS {
-        type_ns: Some((Res::Def(DefKind::Trait, _), _)),
-        macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
+        type_ns: Ok((Res::Def(DefKind::Trait, _), _)),
+        macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
         ..
     } = *ns
     {
@@ -764,8 +965,32 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                         match self.resolve(path_str, ns, &current_item, base_node, &extra_fragment)
                         {
                             Ok(res) => res,
-                            Err(ErrorKind::ResolutionFailure) => {
-                                resolution_failure(cx, &item, path_str, &dox, link_range);
+                            Err(ErrorKind::Resolve(box mut kind)) => {
+                                // We only looked in one namespace. Try to give a better error if possible.
+                                if kind.full_res().is_none() {
+                                    let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
+                                    for &new_ns in &[other_ns, MacroNS] {
+                                        if let Some(res) = self.check_full_res(
+                                            new_ns,
+                                            path_str,
+                                            base_node,
+                                            &current_item,
+                                            &extra_fragment,
+                                        ) {
+                                            kind = ResolutionFailure::WrongNamespace(res, ns);
+                                            break;
+                                        }
+                                    }
+                                }
+                                resolution_failure(
+                                    self,
+                                    &item,
+                                    path_str,
+                                    disambiguator,
+                                    &dox,
+                                    link_range,
+                                    smallvec![kind],
+                                );
                                 // This could just be a normal link or a broken link
                                 // we could potentially check if something is
                                 // "intra-doc-link-like" and warn in that case.
@@ -792,13 +1017,13 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                             ) {
                                 Ok(res) => {
                                     debug!("got res in TypeNS: {:?}", res);
-                                    Some(res)
+                                    Ok(res)
                                 }
                                 Err(ErrorKind::AnchorFailure(msg)) => {
                                     anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
                                     continue;
                                 }
-                                Err(ErrorKind::ResolutionFailure) => None,
+                                Err(ErrorKind::Resolve(box kind)) => Err(kind),
                             },
                             value_ns: match self.resolve(
                                 path_str,
@@ -807,48 +1032,57 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                                 base_node,
                                 &extra_fragment,
                             ) {
-                                Ok(res) => Some(res),
+                                Ok(res) => Ok(res),
                                 Err(ErrorKind::AnchorFailure(msg)) => {
                                     anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
                                     continue;
                                 }
-                                Err(ErrorKind::ResolutionFailure) => None,
+                                Err(ErrorKind::Resolve(box kind)) => Err(kind),
                             }
                             .and_then(|(res, fragment)| {
                                 // Constructors are picked up in the type namespace.
                                 match res {
-                                    Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
+                                    Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => {
+                                        Err(ResolutionFailure::WrongNamespace(res, TypeNS))
+                                    }
                                     _ => match (fragment, extra_fragment) {
                                         (Some(fragment), Some(_)) => {
                                             // Shouldn't happen but who knows?
-                                            Some((res, Some(fragment)))
-                                        }
-                                        (fragment, None) | (None, fragment) => {
-                                            Some((res, fragment))
+                                            Ok((res, Some(fragment)))
                                         }
+                                        (fragment, None) | (None, fragment) => Ok((res, fragment)),
                                     },
                                 }
                             }),
                         };
 
-                        if candidates.is_empty() {
-                            resolution_failure(cx, &item, path_str, &dox, link_range);
+                        let len = candidates.iter().filter(|res| res.is_ok()).count();
+
+                        if len == 0 {
+                            resolution_failure(
+                                self,
+                                &item,
+                                path_str,
+                                disambiguator,
+                                &dox,
+                                link_range,
+                                candidates.into_iter().filter_map(|res| res.err()).collect(),
+                            );
                             // this could just be a normal link
                             continue;
                         }
 
-                        let len = candidates.clone().present_items().count();
-
                         if len == 1 {
-                            candidates.present_items().next().unwrap()
+                            candidates.into_iter().filter_map(|res| res.ok()).next().unwrap()
                         } else if len == 2 && is_derive_trait_collision(&candidates) {
                             candidates.type_ns.unwrap()
                         } else {
                             if is_derive_trait_collision(&candidates) {
-                                candidates.macro_ns = None;
+                                candidates.macro_ns = Err(ResolutionFailure::Dummy);
                             }
+                            // If we're reporting an ambiguity, don't mention the namespaces that failed
                             let candidates =
-                                candidates.map(|candidate| candidate.map(|(res, _)| res));
+                                candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
                             ambiguity_error(
                                 cx,
                                 &item,
@@ -861,11 +1095,33 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                         }
                     }
                     Some(MacroNS) => {
-                        if let Some(res) = self.macro_resolve(path_str, base_node) {
-                            (res, extra_fragment)
-                        } else {
-                            resolution_failure(cx, &item, path_str, &dox, link_range);
-                            continue;
+                        match self.macro_resolve(path_str, base_node) {
+                            Ok(res) => (res, extra_fragment),
+                            Err(mut kind) => {
+                                // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
+                                for &ns in &[TypeNS, ValueNS] {
+                                    if let Some(res) = self.check_full_res(
+                                        ns,
+                                        path_str,
+                                        base_node,
+                                        &current_item,
+                                        &extra_fragment,
+                                    ) {
+                                        kind = ResolutionFailure::WrongNamespace(res, MacroNS);
+                                        break;
+                                    }
+                                }
+                                resolution_failure(
+                                    self,
+                                    &item,
+                                    path_str,
+                                    disambiguator,
+                                    &dox,
+                                    link_range,
+                                    smallvec![kind],
+                                );
+                                continue;
+                            }
                         }
                     }
                 }
@@ -889,7 +1145,7 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                                 path_str,
                                 &dox,
                                 link_range,
-                                AnchorFailure::Primitive,
+                                AnchorFailure::RustdocAnchorConflict(prim),
                             );
                             continue;
                         }
@@ -907,7 +1163,7 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
             let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
                 // The resolved item did not match the disambiguator; give a better error than 'not found'
                 let msg = format!("incompatible link kind for `{}`", path_str);
-                report_diagnostic(cx, &msg, &item, &dox, link_range.clone(), |diag, sp| {
+                report_diagnostic(cx, &msg, &item, &dox, &link_range, |diag, sp| {
                     let note = format!(
                         "this link resolved to {} {}, which is not {} {}",
                         resolved.article(),
@@ -940,7 +1196,7 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                 // Disallow e.g. linking to enums with `struct@`
                 if let Res::Def(kind, _) = res {
                     debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
-                    match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
+                    match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) {
                         | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
                         // NOTE: this allows 'method' to mean both normal functions and associated functions
                         // This can't cause ambiguity because both are in the same namespace.
@@ -1074,21 +1330,16 @@ fn from_res(res: Res) -> Self {
         }
     }
 
-    /// Return (description of the change, suggestion)
-    fn suggestion_for(self, path_str: &str) -> (&'static str, String) {
-        const PREFIX: &str = "prefix with the item kind";
-        const FUNCTION: &str = "add parentheses";
-        const MACRO: &str = "add an exclamation mark";
-
+    fn suggestion(self) -> Suggestion {
         let kind = match self {
-            Disambiguator::Primitive => return (PREFIX, format!("prim@{}", path_str)),
+            Disambiguator::Primitive => return Suggestion::Prefix("prim"),
             Disambiguator::Kind(kind) => kind,
             Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"),
         };
         if kind == DefKind::Macro(MacroKind::Bang) {
-            return (MACRO, format!("{}!", path_str));
+            return Suggestion::Macro;
         } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
-            return (FUNCTION, format!("{}()", path_str));
+            return Suggestion::Function;
         }
 
         let prefix = match kind {
@@ -1113,8 +1364,7 @@ fn suggestion_for(self, path_str: &str) -> (&'static str, String) {
             },
         };
 
-        // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
-        (PREFIX, format!("{}@{}", prefix, path_str))
+        Suggestion::Prefix(prefix)
     }
 
     fn ns(self) -> Namespace {
@@ -1146,6 +1396,31 @@ fn descr(self) -> &'static str {
     }
 }
 
+enum Suggestion {
+    Prefix(&'static str),
+    Function,
+    Macro,
+}
+
+impl Suggestion {
+    fn descr(&self) -> Cow<'static, str> {
+        match self {
+            Self::Prefix(x) => format!("prefix with `{}@`", x).into(),
+            Self::Function => "add parentheses".into(),
+            Self::Macro => "add an exclamation mark".into(),
+        }
+    }
+
+    fn as_help(&self, path_str: &str) -> String {
+        // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
+        match self {
+            Self::Prefix(prefix) => format!("{}@{}", prefix, path_str),
+            Self::Function => format!("{}()", path_str),
+            Self::Macro => format!("{}!", path_str),
+        }
+    }
+}
+
 /// Reports a diagnostic for an intra-doc link.
 ///
 /// If no link range is provided, or the source span of the link cannot be determined, the span of
@@ -1161,7 +1436,7 @@ fn report_diagnostic(
     msg: &str,
     item: &Item,
     dox: &str,
-    link_range: Option<Range<usize>>,
+    link_range: &Option<Range<usize>>,
     decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
 ) {
     let hir_id = match cx.as_local_hir_id(item.def_id) {
@@ -1213,24 +1488,197 @@ fn report_diagnostic(
 }
 
 fn resolution_failure(
-    cx: &DocContext<'_>,
+    collector: &LinkCollector<'_, '_>,
     item: &Item,
     path_str: &str,
+    disambiguator: Option<Disambiguator>,
     dox: &str,
     link_range: Option<Range<usize>>,
+    kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
 ) {
     report_diagnostic(
-        cx,
+        collector.cx,
         &format!("unresolved link to `{}`", path_str),
         item,
         dox,
-        link_range,
+        &link_range,
         |diag, sp| {
-            if let Some(sp) = sp {
-                diag.span_label(sp, "unresolved link");
-            }
+            let in_scope = kinds.iter().any(|kind| kind.res().is_some());
+            let item = |res: Res| {
+                format!(
+                    "the {} `{}`",
+                    res.descr(),
+                    collector.cx.tcx.item_name(res.def_id()).to_string()
+                )
+            };
+            let assoc_item_not_allowed = |res: Res| {
+                let def_id = res.def_id();
+                let name = collector.cx.tcx.item_name(def_id);
+                format!(
+                    "`{}` is {} {}, not a module or type, and cannot have associated items",
+                    name,
+                    res.article(),
+                    res.descr()
+                )
+            };
+            // ignore duplicates
+            let mut variants_seen = SmallVec::<[_; 3]>::new();
+            for mut failure in kinds {
+                // Check if _any_ parent of the path gets resolved.
+                // If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
+                if let ResolutionFailure::NotInScope { module_id, name } = &mut failure {
+                    let mut current = name.as_ref();
+                    loop {
+                        current = match current.rsplitn(2, "::").nth(1) {
+                            Some(p) => p,
+                            None => {
+                                *name = current.to_owned().into();
+                                break;
+                            }
+                        };
+                        if let Some(res) = collector.check_full_res(
+                            TypeNS,
+                            &current,
+                            Some(*module_id),
+                            &None,
+                            &None,
+                        ) {
+                            failure = ResolutionFailure::NoAssocItem(res, Symbol::intern(current));
+                            break;
+                        }
+                    }
+                }
+                let variant = std::mem::discriminant(&failure);
+                if variants_seen.contains(&variant) {
+                    continue;
+                }
+                variants_seen.push(variant);
+                let note = match failure {
+                    ResolutionFailure::NotInScope { module_id, name, .. } => {
+                        if in_scope {
+                            continue;
+                        }
+                        // NOTE: uses an explicit `continue` so the `note:` will come before the `help:`
+                        let module_name = collector.cx.tcx.item_name(module_id);
+                        let note = format!("no item named `{}` in `{}`", name, module_name);
+                        if let Some(span) = sp {
+                            diag.span_label(span, &note);
+                        } else {
+                            diag.note(&note);
+                        }
+                        // If the link has `::` in the path, assume it's meant to be an intra-doc link
+                        if !path_str.contains("::") {
+                            // Otherwise, the `[]` might be unrelated.
+                            // FIXME(https://github.com/raphlinus/pulldown-cmark/issues/373):
+                            // don't show this for autolinks (`<>`), `()` style links, or reference links
+                            diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
+                        }
+                        continue;
+                    }
+                    ResolutionFailure::Dummy => continue,
+                    ResolutionFailure::WrongNamespace(res, expected_ns) => {
+                        if let Res::Def(kind, _) = res {
+                            let disambiguator = Disambiguator::Kind(kind);
+                            suggest_disambiguator(
+                                disambiguator,
+                                diag,
+                                path_str,
+                                dox,
+                                sp,
+                                &link_range,
+                            )
+                        }
 
-            diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
+                        format!(
+                            "this link resolves to {}, which is not in the {} namespace",
+                            item(res),
+                            expected_ns.descr()
+                        )
+                    }
+                    ResolutionFailure::NoParentItem => {
+                        diag.level = rustc_errors::Level::Bug;
+                        "all intra doc links should have a parent item".to_owned()
+                    }
+                    ResolutionFailure::NoPrimitiveImpl(res, _) => format!(
+                        "this link partially resolves to {}, which does not have any associated items",
+                        item(res),
+                    ),
+                    ResolutionFailure::NoPrimitiveAssocItem { prim_name, assoc_item, .. } => {
+                        format!(
+                            "the builtin type `{}` does not have an associated item named `{}`",
+                            prim_name, assoc_item
+                        )
+                    }
+                    ResolutionFailure::NoAssocItem(res, assoc_item) => {
+                        use DefKind::*;
+
+                        let (kind, def_id) = match res {
+                            Res::Def(kind, def_id) => (kind, def_id),
+                            x => unreachable!(
+                                "primitives are covered above and other `Res` variants aren't possible at module scope: {:?}",
+                                x,
+                            ),
+                        };
+                        let name = collector.cx.tcx.item_name(def_id);
+                        let path_description = if let Some(disambiguator) = disambiguator {
+                            disambiguator.descr()
+                        } else {
+                            match kind {
+                                Mod | ForeignMod => "inner item",
+                                Struct => "field or associated item",
+                                Enum | Union => "variant or associated item",
+                                Variant
+                                | Field
+                                | Closure
+                                | Generator
+                                | AssocTy
+                                | AssocConst
+                                | AssocFn
+                                | Fn
+                                | Macro(_)
+                                | Const
+                                | ConstParam
+                                | ExternCrate
+                                | Use
+                                | LifetimeParam
+                                | Ctor(_, _)
+                                | AnonConst => {
+                                    let note = assoc_item_not_allowed(res);
+                                    if let Some(span) = sp {
+                                        diag.span_label(span, &note);
+                                    } else {
+                                        diag.note(&note);
+                                    }
+                                    return;
+                                }
+                                Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
+                                | Static => "associated item",
+                                Impl | GlobalAsm => unreachable!("not a path"),
+                            }
+                        };
+                        format!(
+                            "the {} `{}` has no {} named `{}`",
+                            res.descr(),
+                            name,
+                            path_description,
+                            assoc_item
+                        )
+                    }
+                    ResolutionFailure::CannotHaveAssociatedItems(res, _) => {
+                        assoc_item_not_allowed(res)
+                    }
+                    ResolutionFailure::NotAVariant(res, variant) => format!(
+                        "this link partially resolves to {}, but there is no variant named {}",
+                        item(res),
+                        variant
+                    ),
+                };
+                if let Some(span) = sp {
+                    diag.span_label(span, &note);
+                } else {
+                    diag.note(&note);
+                }
+            }
         },
     );
 }
@@ -1245,31 +1693,14 @@ fn anchor_failure(
 ) {
     let msg = match failure {
         AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str),
-        AnchorFailure::Primitive
-        | AnchorFailure::Variant
-        | AnchorFailure::AssocConstant
-        | AnchorFailure::AssocType
-        | AnchorFailure::Field
-        | AnchorFailure::Method => {
-            let kind = match failure {
-                AnchorFailure::Primitive => "primitive type",
-                AnchorFailure::Variant => "enum variant",
-                AnchorFailure::AssocConstant => "associated constant",
-                AnchorFailure::AssocType => "associated type",
-                AnchorFailure::Field => "struct field",
-                AnchorFailure::Method => "method",
-                AnchorFailure::MultipleAnchors => unreachable!("should be handled already"),
-            };
-
-            format!(
-                "`{}` contains an anchor, but links to {kind}s are already anchored",
-                path_str,
-                kind = kind
-            )
-        }
+        AnchorFailure::RustdocAnchorConflict(res) => format!(
+            "`{}` contains an anchor, but links to {kind}s are already anchored",
+            path_str,
+            kind = res.descr(),
+        ),
     };
 
-    report_diagnostic(cx, &msg, item, dox, link_range, |diag, sp| {
+    report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| {
         if let Some(sp) = sp {
             diag.span_label(sp, "contains invalid anchor");
         }
@@ -1308,7 +1739,7 @@ fn ambiguity_error(
         }
     }
 
-    report_diagnostic(cx, &msg, item, dox, link_range.clone(), |diag, sp| {
+    report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| {
         if let Some(sp) = sp {
             diag.span_label(sp, "ambiguous link");
         } else {
@@ -1330,18 +1761,20 @@ fn suggest_disambiguator(
     sp: Option<rustc_span::Span>,
     link_range: &Option<Range<usize>>,
 ) {
-    let (action, mut suggestion) = disambiguator.suggestion_for(path_str);
-    let help = format!("to link to the {}, {}", disambiguator.descr(), action);
+    let suggestion = disambiguator.suggestion();
+    let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
 
     if let Some(sp) = sp {
         let link_range = link_range.as_ref().expect("must have a link range if we have a span");
-        if dox.bytes().nth(link_range.start) == Some(b'`') {
-            suggestion = format!("`{}`", suggestion);
-        }
+        let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
+            format!("`{}`", suggestion.as_help(path_str))
+        } else {
+            suggestion.as_help(path_str)
+        };
 
-        diag.span_suggestion(sp, &help, suggestion, Applicability::MaybeIncorrect);
+        diag.span_suggestion(sp, &help, msg, Applicability::MaybeIncorrect);
     } else {
-        diag.help(&format!("{}: {}", help, suggestion));
+        diag.help(&format!("{}: {}", help, suggestion.as_help(path_str)));
     }
 }
 
@@ -1356,7 +1789,7 @@ fn privacy_error(
     let msg =
         format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
 
-    report_diagnostic(cx, &msg, item, dox, link_range, |diag, sp| {
+    report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| {
         if let Some(sp) = sp {
             diag.span_label(sp, "this item is private");
         }
@@ -1375,16 +1808,16 @@ fn handle_variant(
     cx: &DocContext<'_>,
     res: Res,
     extra_fragment: &Option<String>,
-) -> Result<(Res, Option<String>), ErrorKind> {
+) -> Result<(Res, Option<String>), ErrorKind<'static>> {
     use rustc_middle::ty::DefIdTree;
 
     if extra_fragment.is_some() {
-        return Err(ErrorKind::AnchorFailure(AnchorFailure::Variant));
+        return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res)));
     }
     let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
         parent
     } else {
-        return Err(ErrorKind::ResolutionFailure);
+        return Err(ResolutionFailure::NoParentItem.into());
     };
     let parent_def = Res::Def(DefKind::Enum, parent);
     let variant = cx.tcx.expect_variant_res(res);
index 8827c9351a62defc8c202c6542b851404a4c2322..92d27179e8c3fbce95bd647e251ad1ad6d69558a 100644 (file)
@@ -2,14 +2,13 @@ error: unresolved link to `S::fmt`
   --> $DIR/assoc-item-not-in-scope.rs:4:14
    |
 LL | /// Link to [`S::fmt`]
-   |              ^^^^^^^^ unresolved link
+   |              ^^^^^^^^ the struct `S` has no field or associated item named `fmt`
    |
 note: the lint level is defined here
   --> $DIR/assoc-item-not-in-scope.rs:1:9
    |
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: aborting due to previous error
 
index 7530e3ad0f551f50ff841ff8d8e47c9b131a061e..5020b97b2f20101221466fbc5cf9bdb392401a29 100644 (file)
@@ -2,7 +2,7 @@ error: unresolved link to `v2`
   --> $DIR/deny-intra-link-resolution-failure.rs:3:6
    |
 LL | /// [v2]
-   |      ^^ unresolved link
+   |      ^^ no item named `v2` in `deny_intra_link_resolution_failure`
    |
 note: the lint level is defined here
   --> $DIR/deny-intra-link-resolution-failure.rs:1:9
index f1c07e31cd7530c6fe0f8df12b703b53488e8307..771fc2204f5f84a60581c5127c2e75e729a20d64 100644 (file)
@@ -2,14 +2,13 @@ error: unresolved link to `TypeAlias::hoge`
   --> $DIR/intra-doc-alias-ice.rs:5:30
    |
 LL | /// [broken cross-reference](TypeAlias::hoge)
-   |                              ^^^^^^^^^^^^^^^ unresolved link
+   |                              ^^^^^^^^^^^^^^^ the type alias `TypeAlias` has no associated item named `hoge`
    |
 note: the lint level is defined here
   --> $DIR/intra-doc-alias-ice.rs:1:9
    |
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: aborting due to previous error
 
diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs
new file mode 100644 (file)
index 0000000..26b629b
--- /dev/null
@@ -0,0 +1,88 @@
+#![deny(broken_intra_doc_links)]
+//~^ NOTE lint level is defined
+
+// FIXME: this should say that it was skipped (maybe an allowed by default lint?)
+/// [<invalid syntax>]
+
+/// [path::to::nonexistent::module]
+//~^ ERROR unresolved link
+//~| NOTE no item named `path` in `intra_link_errors`
+
+/// [path::to::nonexistent::macro!]
+//~^ ERROR unresolved link
+//~| NOTE no item named `path` in `intra_link_errors`
+
+/// [type@path::to::nonexistent::type]
+//~^ ERROR unresolved link
+//~| NOTE no item named `path` in `intra_link_errors`
+
+/// [std::io::not::here]
+//~^ ERROR unresolved link
+//~| NOTE the module `io` has no inner item
+
+/// [std::io::Error::x]
+//~^ ERROR unresolved link
+//~| NOTE the struct `Error` has no field
+
+/// [std::io::ErrorKind::x]
+//~^ ERROR unresolved link
+//~| NOTE the enum `ErrorKind` has no variant
+
+/// [f::A]
+//~^ ERROR unresolved link
+//~| NOTE `f` is a function, not a module
+
+/// [S::A]
+//~^ ERROR unresolved link
+//~| NOTE struct `S` has no field or associated item
+
+/// [S::fmt]
+//~^ ERROR unresolved link
+//~| NOTE struct `S` has no field or associated item
+
+/// [E::D]
+//~^ ERROR unresolved link
+//~| NOTE enum `E` has no variant or associated item
+
+/// [u8::not_found]
+//~^ ERROR unresolved link
+//~| NOTE the builtin type `u8` does not have an associated item named `not_found`
+
+/// [S!]
+//~^ ERROR unresolved link
+//~| HELP to link to the struct, prefix with `struct@`
+//~| NOTE this link resolves to the struct `S`
+pub fn f() {}
+#[derive(Debug)]
+pub struct S;
+
+pub enum E { A, B, C }
+
+/// [type@S::h]
+//~^ ERROR unresolved link
+//~| HELP to link to the associated function
+//~| NOTE not in the type namespace
+impl S {
+    pub fn h() {}
+}
+
+/// [type@T::g]
+//~^ ERROR unresolved link
+//~| HELP to link to the associated function
+//~| NOTE not in the type namespace
+
+/// [T::h!]
+//~^ ERROR unresolved link
+//~| NOTE `T` has no macro named `h`
+pub trait T {
+    fn g() {}
+}
+
+/// [m()]
+//~^ ERROR unresolved link
+//~| HELP to link to the macro
+//~| NOTE not in the value namespace
+#[macro_export]
+macro_rules! m {
+    () => {};
+}
diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr
new file mode 100644 (file)
index 0000000..fbf3dcb
--- /dev/null
@@ -0,0 +1,116 @@
+error: unresolved link to `path::to::nonexistent::module`
+  --> $DIR/intra-link-errors.rs:7:6
+   |
+LL | /// [path::to::nonexistent::module]
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors`
+   |
+note: the lint level is defined here
+  --> $DIR/intra-link-errors.rs:1:9
+   |
+LL | #![deny(broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unresolved link to `path::to::nonexistent::macro`
+  --> $DIR/intra-link-errors.rs:11:6
+   |
+LL | /// [path::to::nonexistent::macro!]
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors`
+
+error: unresolved link to `path::to::nonexistent::type`
+  --> $DIR/intra-link-errors.rs:15:6
+   |
+LL | /// [type@path::to::nonexistent::type]
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors`
+
+error: unresolved link to `std::io::not::here`
+  --> $DIR/intra-link-errors.rs:19:6
+   |
+LL | /// [std::io::not::here]
+   |      ^^^^^^^^^^^^^^^^^^ the module `io` has no inner item named `not`
+
+error: unresolved link to `std::io::Error::x`
+  --> $DIR/intra-link-errors.rs:23:6
+   |
+LL | /// [std::io::Error::x]
+   |      ^^^^^^^^^^^^^^^^^ the struct `Error` has no field or associated item named `x`
+
+error: unresolved link to `std::io::ErrorKind::x`
+  --> $DIR/intra-link-errors.rs:27:6
+   |
+LL | /// [std::io::ErrorKind::x]
+   |      ^^^^^^^^^^^^^^^^^^^^^ the enum `ErrorKind` has no variant or associated item named `x`
+
+error: unresolved link to `f::A`
+  --> $DIR/intra-link-errors.rs:31:6
+   |
+LL | /// [f::A]
+   |      ^^^^ `f` is a function, not a module or type, and cannot have associated items
+
+error: unresolved link to `S::A`
+  --> $DIR/intra-link-errors.rs:35:6
+   |
+LL | /// [S::A]
+   |      ^^^^ the struct `S` has no field or associated item named `A`
+
+error: unresolved link to `S::fmt`
+  --> $DIR/intra-link-errors.rs:39:6
+   |
+LL | /// [S::fmt]
+   |      ^^^^^^ the struct `S` has no field or associated item named `fmt`
+
+error: unresolved link to `E::D`
+  --> $DIR/intra-link-errors.rs:43:6
+   |
+LL | /// [E::D]
+   |      ^^^^ the enum `E` has no variant or associated item named `D`
+
+error: unresolved link to `u8::not_found`
+  --> $DIR/intra-link-errors.rs:47:6
+   |
+LL | /// [u8::not_found]
+   |      ^^^^^^^^^^^^^ the builtin type `u8` does not have an associated item named `not_found`
+
+error: unresolved link to `S`
+  --> $DIR/intra-link-errors.rs:51:6
+   |
+LL | /// [S!]
+   |      ^^
+   |      |
+   |      this link resolves to the struct `S`, which is not in the macro namespace
+   |      help: to link to the struct, prefix with `struct@`: `struct@S`
+
+error: unresolved link to `T::g`
+  --> $DIR/intra-link-errors.rs:69:6
+   |
+LL | /// [type@T::g]
+   |      ^^^^^^^^^
+   |      |
+   |      this link resolves to the associated function `g`, which is not in the type namespace
+   |      help: to link to the associated function, add parentheses: `T::g()`
+
+error: unresolved link to `T::h`
+  --> $DIR/intra-link-errors.rs:74:6
+   |
+LL | /// [T::h!]
+   |      ^^^^^ the trait `T` has no macro named `h`
+
+error: unresolved link to `S::h`
+  --> $DIR/intra-link-errors.rs:61:6
+   |
+LL | /// [type@S::h]
+   |      ^^^^^^^^^
+   |      |
+   |      this link resolves to the associated function `h`, which is not in the type namespace
+   |      help: to link to the associated function, add parentheses: `S::h()`
+
+error: unresolved link to `m`
+  --> $DIR/intra-link-errors.rs:81:6
+   |
+LL | /// [m()]
+   |      ^^^
+   |      |
+   |      this link resolves to the macro `m`, which is not in the value namespace
+   |      help: to link to the macro, add an exclamation mark: `m!`
+
+error: aborting due to 16 previous errors
+
index 548d3e2544a00aa12699ecccf231a56f94e4ef6e..85738ceae8e615b34eeedffa8a0ffb35d9e1b0ac 100644 (file)
 
 /// [struct@char]
 //~^ ERROR incompatible link
-//~| HELP prefix with the item kind
+//~| HELP prefix with `mod@`
 //~| NOTE resolved to a module
 pub mod char {}
 
 pub mod inner {
     //! [struct@char]
     //~^ ERROR incompatible link
-    //~| HELP prefix with the item kind
+    //~| HELP prefix with `prim@`
     //~| NOTE resolved to a builtin type
 }
index 53dccfbf1a2c48a7d2c533ee69bb0f842145e38d..43587a80021afe8c1937613808f3f92a1a09eef2 100644 (file)
@@ -9,11 +9,11 @@ note: the lint level is defined here
    |
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
-help: to link to the module, prefix with the item kind
+help: to link to the module, prefix with `mod@`
    |
 LL | /// [mod@char]
    |      ^^^^^^^^
-help: to link to the builtin type, prefix with the item kind
+help: to link to the builtin type, prefix with `prim@`
    |
 LL | /// [prim@char]
    |      ^^^^^^^^^
@@ -24,11 +24,11 @@ error: `char` is both a module and a builtin type
 LL | /// [type@char]
    |      ^^^^^^^^^ ambiguous link
    |
-help: to link to the module, prefix with the item kind
+help: to link to the module, prefix with `mod@`
    |
 LL | /// [mod@char]
    |      ^^^^^^^^
-help: to link to the builtin type, prefix with the item kind
+help: to link to the builtin type, prefix with `prim@`
    |
 LL | /// [prim@char]
    |      ^^^^^^^^^
@@ -37,25 +37,17 @@ error: incompatible link kind for `char`
   --> $DIR/intra-link-prim-conflict.rs:19:6
    |
 LL | /// [struct@char]
-   |      ^^^^^^^^^^^
+   |      ^^^^^^^^^^^ help: to link to the module, prefix with `mod@`: `mod@char`
    |
    = note: this link resolved to a module, which is not a struct
-help: to link to the module, prefix with the item kind
-   |
-LL | /// [mod@char]
-   |      ^^^^^^^^
 
 error: incompatible link kind for `char`
   --> $DIR/intra-link-prim-conflict.rs:26:10
    |
 LL |     //! [struct@char]
-   |          ^^^^^^^^^^^
+   |          ^^^^^^^^^^^ help: to link to the builtin type, prefix with `prim@`: `prim@char`
    |
    = note: this link resolved to a builtin type, which is not a struct
-help: to link to the builtin type, prefix with the item kind
-   |
-LL |     //! [prim@char]
-   |          ^^^^^^^^^
 
 error: aborting due to 4 previous errors
 
index 6b0ff8f11629505089e2b2945e8da8511b12cc17..3c13df20588d8ae372ae487817b4730116de8f4a 100644 (file)
@@ -2,7 +2,7 @@ error: unresolved link to `i`
   --> $DIR/intra-link-span-ice-55723.rs:9:10
    |
 LL | /// (arr[i])
-   |           ^ unresolved link
+   |           ^ no item named `i` in `intra_link_span_ice_55723`
    |
 note: the lint level is defined here
   --> $DIR/intra-link-span-ice-55723.rs:1:9
index 7912c046f1c78f8e481f9c2a1bdecc6fa8b990d5..17891ca05efa18ca43aa7dcf722df85fb13bd5fa 100644 (file)
@@ -9,7 +9,7 @@ note: the lint level is defined here
    |
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
-help: to link to the struct, prefix with the item kind
+help: to link to the struct, prefix with `struct@`
    |
 LL | /// [`struct@ambiguous`] is ambiguous.
    |      ^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ error: `ambiguous` is both a struct and a function
 LL | /// [ambiguous] is ambiguous.
    |      ^^^^^^^^^ ambiguous link
    |
-help: to link to the struct, prefix with the item kind
+help: to link to the struct, prefix with `struct@`
    |
 LL | /// [struct@ambiguous] is ambiguous.
    |      ^^^^^^^^^^^^^^^^
@@ -39,7 +39,7 @@ error: `multi_conflict` is a struct, a function, and a macro
 LL | /// [`multi_conflict`] is a three-way conflict.
    |      ^^^^^^^^^^^^^^^^ ambiguous link
    |
-help: to link to the struct, prefix with the item kind
+help: to link to the struct, prefix with `struct@`
    |
 LL | /// [`struct@multi_conflict`] is a three-way conflict.
    |      ^^^^^^^^^^^^^^^^^^^^^^^
@@ -58,11 +58,11 @@ error: `type_and_value` is both a module and a constant
 LL | /// Ambiguous [type_and_value].
    |                ^^^^^^^^^^^^^^ ambiguous link
    |
-help: to link to the module, prefix with the item kind
+help: to link to the module, prefix with `mod@`
    |
 LL | /// Ambiguous [mod@type_and_value].
    |                ^^^^^^^^^^^^^^^^^^
-help: to link to the constant, prefix with the item kind
+help: to link to the constant, prefix with `const@`
    |
 LL | /// Ambiguous [const@type_and_value].
    |                ^^^^^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ error: `foo::bar` is both an enum and a function
 LL | /// Ambiguous non-implied shortcut link [`foo::bar`].
    |                                          ^^^^^^^^^^ ambiguous link
    |
-help: to link to the enum, prefix with the item kind
+help: to link to the enum, prefix with `enum@`
    |
 LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`].
    |                                          ^^^^^^^^^^^^^^^
index e737b84320d944a4d8db53561fac5d0145c24a4f..1825a4ad1fa6b6c013b8fc652d135dd69b0093d3 100644 (file)
@@ -1,4 +1,4 @@
-error: `Foo::f#hola` contains an anchor, but links to struct fields are already anchored
+error: `Foo::f#hola` contains an anchor, but links to fields are already anchored
   --> $DIR/intra-links-anchors.rs:25:15
    |
 LL | /// Or maybe [Foo::f#hola].
@@ -16,13 +16,13 @@ error: `hello#people#!` contains multiple anchors
 LL | /// Another anchor error: [hello#people#!].
    |                            ^^^^^^^^^^^^^^ contains invalid anchor
 
-error: `Enum::A#whatever` contains an anchor, but links to enum variants are already anchored
+error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored
   --> $DIR/intra-links-anchors.rs:37:28
    |
 LL | /// Damn enum's variants: [Enum::A#whatever].
    |                            ^^^^^^^^^^^^^^^^ contains invalid anchor
 
-error: `u32#hello` contains an anchor, but links to primitive types are already anchored
+error: `u32#hello` contains an anchor, but links to builtin types are already anchored
   --> $DIR/intra-links-anchors.rs:43:6
    |
 LL | /// [u32#hello]
index 54e507adfe550e3036fea159d9fbb75efb4fbb71..b9c8e033b1b21002ceb70070deeb30b5b991a5c7 100644 (file)
@@ -14,27 +14,27 @@ trait T {}
 /// Link to [struct@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `enum@`
 
 /// Link to [mod@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `enum@`
 
 /// Link to [union@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `enum@`
 
 /// Link to [trait@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `enum@`
 
 /// Link to [struct@T]
 //~^ ERROR incompatible link kind for `T`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `trait@`
 
 /// Link to [derive@m]
 //~^ ERROR incompatible link kind for `m`
@@ -44,22 +44,22 @@ trait T {}
 /// Link to [const@s]
 //~^ ERROR incompatible link kind for `s`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `static@`
 
 /// Link to [static@c]
 //~^ ERROR incompatible link kind for `c`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `const@`
 
 /// Link to [fn@c]
 //~^ ERROR incompatible link kind for `c`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `const@`
 
 /// Link to [c()]
 //~^ ERROR incompatible link kind for `c`
 //~| NOTE this link resolved
-//~| HELP prefix with the item kind
+//~| HELP prefix with `const@`
 
 /// Link to [const@f]
 //~^ ERROR incompatible link kind for `f`
index 27b94af0378c240cf4e296ce62cd708e210a2cf1..2e732baf6e01e246fd0ae317b212eb755caab450 100644 (file)
@@ -2,7 +2,7 @@ error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:14:14
    |
 LL | /// Link to [struct@S]
-   |              ^^^^^^^^
+   |              ^^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S`
    |
 note: the lint level is defined here
   --> $DIR/intra-links-disambiguator-mismatch.rs:1:9
@@ -10,58 +10,38 @@ note: the lint level is defined here
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
    = note: this link resolved to an enum, which is not a struct
-help: to link to the enum, prefix with the item kind
-   |
-LL | /// Link to [enum@S]
-   |              ^^^^^^
 
 error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:19:14
    |
 LL | /// Link to [mod@S]
-   |              ^^^^^
+   |              ^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S`
    |
    = note: this link resolved to an enum, which is not a module
-help: to link to the enum, prefix with the item kind
-   |
-LL | /// Link to [enum@S]
-   |              ^^^^^^
 
 error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:24:14
    |
 LL | /// Link to [union@S]
-   |              ^^^^^^^
+   |              ^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S`
    |
    = note: this link resolved to an enum, which is not a union
-help: to link to the enum, prefix with the item kind
-   |
-LL | /// Link to [enum@S]
-   |              ^^^^^^
 
 error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:29:14
    |
 LL | /// Link to [trait@S]
-   |              ^^^^^^^
+   |              ^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S`
    |
    = note: this link resolved to an enum, which is not a trait
-help: to link to the enum, prefix with the item kind
-   |
-LL | /// Link to [enum@S]
-   |              ^^^^^^
 
 error: incompatible link kind for `T`
   --> $DIR/intra-links-disambiguator-mismatch.rs:34:14
    |
 LL | /// Link to [struct@T]
-   |              ^^^^^^^^
+   |              ^^^^^^^^ help: to link to the trait, prefix with `trait@`: `trait@T`
    |
    = note: this link resolved to a trait, which is not a struct
-help: to link to the trait, prefix with the item kind
-   |
-LL | /// Link to [trait@T]
-   |              ^^^^^^^
 
 error: incompatible link kind for `m`
   --> $DIR/intra-links-disambiguator-mismatch.rs:39:14
@@ -75,49 +55,33 @@ error: incompatible link kind for `s`
   --> $DIR/intra-links-disambiguator-mismatch.rs:44:14
    |
 LL | /// Link to [const@s]
-   |              ^^^^^^^
+   |              ^^^^^^^ help: to link to the static, prefix with `static@`: `static@s`
    |
    = note: this link resolved to a static, which is not a constant
-help: to link to the static, prefix with the item kind
-   |
-LL | /// Link to [static@s]
-   |              ^^^^^^^^
 
 error: incompatible link kind for `c`
   --> $DIR/intra-links-disambiguator-mismatch.rs:49:14
    |
 LL | /// Link to [static@c]
-   |              ^^^^^^^^
+   |              ^^^^^^^^ help: to link to the constant, prefix with `const@`: `const@c`
    |
    = note: this link resolved to a constant, which is not a static
-help: to link to the constant, prefix with the item kind
-   |
-LL | /// Link to [const@c]
-   |              ^^^^^^^
 
 error: incompatible link kind for `c`
   --> $DIR/intra-links-disambiguator-mismatch.rs:54:14
    |
 LL | /// Link to [fn@c]
-   |              ^^^^
+   |              ^^^^ help: to link to the constant, prefix with `const@`: `const@c`
    |
    = note: this link resolved to a constant, which is not a function
-help: to link to the constant, prefix with the item kind
-   |
-LL | /// Link to [const@c]
-   |              ^^^^^^^
 
 error: incompatible link kind for `c`
   --> $DIR/intra-links-disambiguator-mismatch.rs:59:14
    |
 LL | /// Link to [c()]
-   |              ^^^
+   |              ^^^ help: to link to the constant, prefix with `const@`: `const@c`
    |
    = note: this link resolved to a constant, which is not a function
-help: to link to the constant, prefix with the item kind
-   |
-LL | /// Link to [const@c]
-   |              ^^^^^^^
 
 error: incompatible link kind for `f`
   --> $DIR/intra-links-disambiguator-mismatch.rs:64:14
index 1e3a26fadfa9fc3edc02a2587b1af6b9ebda5d83..351f8fafa64d8117b38279e3269dab8f655980b6 100644 (file)
@@ -2,7 +2,7 @@ warning: unresolved link to `error`
   --> $DIR/intra-links-warning-crlf.rs:7:6
    |
 LL | /// [error]
-   |      ^^^^^ unresolved link
+   |      ^^^^^ no item named `error` in `intra_links_warning_crlf`
    |
    = note: `#[warn(broken_intra_doc_links)]` on by default
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@@ -11,7 +11,7 @@ warning: unresolved link to `error1`
   --> $DIR/intra-links-warning-crlf.rs:12:11
    |
 LL | /// docs [error1]
-   |           ^^^^^^ unresolved link
+   |           ^^^^^^ no item named `error1` in `intra_links_warning_crlf`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -19,7 +19,7 @@ warning: unresolved link to `error2`
   --> $DIR/intra-links-warning-crlf.rs:15:11
    |
 LL | /// docs [error2]
-   |           ^^^^^^ unresolved link
+   |           ^^^^^^ no item named `error2` in `intra_links_warning_crlf`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -27,7 +27,7 @@ warning: unresolved link to `error`
   --> $DIR/intra-links-warning-crlf.rs:23:20
    |
 LL |  * It also has an [error].
-   |                    ^^^^^ unresolved link
+   |                    ^^^^^ no item named `error` in `intra_links_warning_crlf`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
index 53f2476295ebbbd472eacd13cdbec4b12717cdff..0832e00d35a005b13093cee326e002d5c13d9725 100644 (file)
@@ -2,56 +2,45 @@ warning: unresolved link to `Foo::baz`
   --> $DIR/intra-links-warning.rs:3:23
    |
 LL |        //! Test with [Foo::baz], [Bar::foo], ...
-   |                       ^^^^^^^^ unresolved link
+   |                       ^^^^^^^^ the struct `Foo` has no field or associated item named `baz`
    |
    = note: `#[warn(broken_intra_doc_links)]` on by default
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 warning: unresolved link to `Bar::foo`
   --> $DIR/intra-links-warning.rs:3:35
    |
 LL |        //! Test with [Foo::baz], [Bar::foo], ...
-   |                                   ^^^^^^^^ unresolved link
-   |
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
+   |                                   ^^^^^^^^ no item named `Bar` in `intra_links_warning`
 
 warning: unresolved link to `Uniooon::X`
   --> $DIR/intra-links-warning.rs:6:13
    |
 LL |      //! , [Uniooon::X] and [Qux::Z].
-   |             ^^^^^^^^^^ unresolved link
-   |
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
+   |             ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning`
 
 warning: unresolved link to `Qux::Z`
   --> $DIR/intra-links-warning.rs:6:30
    |
 LL |      //! , [Uniooon::X] and [Qux::Z].
-   |                              ^^^^^^ unresolved link
-   |
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
+   |                              ^^^^^^ no item named `Qux` in `intra_links_warning`
 
 warning: unresolved link to `Uniooon::X`
   --> $DIR/intra-links-warning.rs:10:14
    |
 LL |       //! , [Uniooon::X] and [Qux::Z].
-   |              ^^^^^^^^^^ unresolved link
-   |
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
+   |              ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning`
 
 warning: unresolved link to `Qux::Z`
   --> $DIR/intra-links-warning.rs:10:31
    |
 LL |       //! , [Uniooon::X] and [Qux::Z].
-   |                               ^^^^^^ unresolved link
-   |
-   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
+   |                               ^^^^^^ no item named `Qux` in `intra_links_warning`
 
 warning: unresolved link to `Qux:Y`
   --> $DIR/intra-links-warning.rs:14:13
    |
 LL |        /// [Qux:Y]
-   |             ^^^^^ unresolved link
+   |             ^^^^^ no item named `Qux:Y` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -59,7 +48,7 @@ warning: unresolved link to `error`
   --> $DIR/intra-links-warning.rs:58:30
    |
 LL |  * time to introduce a link [error]*/
-   |                              ^^^^^ unresolved link
+   |                              ^^^^^ no item named `error` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -67,7 +56,7 @@ warning: unresolved link to `error`
   --> $DIR/intra-links-warning.rs:64:30
    |
 LL |  * time to introduce a link [error]
-   |                              ^^^^^ unresolved link
+   |                              ^^^^^ no item named `error` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -81,6 +70,7 @@ LL | #[doc = "single line [error]"]
            
            single line [error]
                         ^^^^^
+   = note: no item named `error` in `intra_links_warning`
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 warning: unresolved link to `error`
@@ -93,6 +83,7 @@ LL | #[doc = "single line with \"escaping\" [error]"]
            
            single line with "escaping" [error]
                                         ^^^^^
+   = note: no item named `error` in `intra_links_warning`
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 warning: unresolved link to `error`
@@ -107,13 +98,14 @@ LL | | /// [error]
            
            [error]
             ^^^^^
+   = note: no item named `error` in `intra_links_warning`
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 warning: unresolved link to `error1`
   --> $DIR/intra-links-warning.rs:80:11
    |
 LL | /// docs [error1]
-   |           ^^^^^^ unresolved link
+   |           ^^^^^^ no item named `error1` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -121,7 +113,7 @@ warning: unresolved link to `error2`
   --> $DIR/intra-links-warning.rs:82:11
    |
 LL | /// docs [error2]
-   |           ^^^^^^ unresolved link
+   |           ^^^^^^ no item named `error2` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -129,7 +121,7 @@ warning: unresolved link to `BarA`
   --> $DIR/intra-links-warning.rs:21:10
    |
 LL | /// bar [BarA] bar
-   |          ^^^^ unresolved link
+   |          ^^^^ no item named `BarA` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -137,7 +129,7 @@ warning: unresolved link to `BarB`
   --> $DIR/intra-links-warning.rs:27:9
    |
 LL |  * bar [BarB] bar
-   |         ^^^^ unresolved link
+   |         ^^^^ no item named `BarB` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -145,7 +137,7 @@ warning: unresolved link to `BarC`
   --> $DIR/intra-links-warning.rs:34:6
    |
 LL | bar [BarC] bar
-   |      ^^^^ unresolved link
+   |      ^^^^ no item named `BarC` in `intra_links_warning`
    |
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
@@ -159,6 +151,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"]
            
            bar [BarD] bar
                 ^^^^
+   = note: no item named `BarD` in `intra_links_warning`
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 warning: unresolved link to `BarF`
@@ -174,6 +167,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz");
            
            bar [BarF] bar
                 ^^^^
+   = note: no item named `BarF` in `intra_links_warning`
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
    = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
index 04296d2e44a7ce56d2919b90e4b4313dfb27dd22..550b79f6e89286bee7166d439514e661704b90cd 100644 (file)
@@ -32,7 +32,7 @@ error: unresolved link to `error`
   --> $DIR/lint-group.rs:9:29
    |
 LL | /// what up, let's make an [error]
-   |                             ^^^^^ unresolved link
+   |                             ^^^^^ no item named `error` in `lint_group`
    |
 note: the lint level is defined here
   --> $DIR/lint-group.rs:7:9
diff --git a/src/test/rustdoc/const-generics/type-alias.rs b/src/test/rustdoc/const-generics/type-alias.rs
new file mode 100644 (file)
index 0000000..3064d07
--- /dev/null
@@ -0,0 +1,6 @@
+// ignore-tidy-linelength
+#![feature(min_const_generics)]
+#![crate_name = "foo"]
+
+// @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex<const D: usize> = [i64; D];'
+pub type CellIndex<const D: usize> = [i64; D];
diff --git a/src/test/ui/const-generics/invalid-enum.rs b/src/test/ui/const-generics/invalid-enum.rs
new file mode 100644 (file)
index 0000000..ceb188a
--- /dev/null
@@ -0,0 +1,39 @@
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+#[derive(PartialEq, Eq)]
+enum CompileFlag {
+  A,
+  B,
+}
+
+pub fn test_1<const CF: CompileFlag>() {}
+pub fn test_2<T, const CF: CompileFlag>(x: T) {}
+pub struct Example<const CF: CompileFlag, T=u32>{
+  x: T,
+}
+
+impl<const CF: CompileFlag, T> Example<CF, T> {
+  const ASSOC_FLAG: CompileFlag = CompileFlag::A;
+}
+
+pub fn main() {
+  test_1::<CompileFlag::A>();
+  //~^ ERROR: expected type, found variant
+  //~| ERROR: wrong number of const arguments
+  //~| ERROR: wrong number of type arguments
+
+  test_2::<_, CompileFlag::A>(0);
+  //~^ ERROR: expected type, found variant
+  //~| ERROR: wrong number of const arguments
+  //~| ERROR: wrong number of type arguments
+
+  let _: Example<CompileFlag::A, _> = Example { x: 0 };
+  //~^ ERROR: expected type, found variant
+  //~| ERROR: wrong number of const arguments
+  //~| ERROR: wrong number of type arguments
+
+  let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
+  //~^ ERROR: wrong number of const arguments
+  //~| ERROR: wrong number of type arguments
+}
diff --git a/src/test/ui/const-generics/invalid-enum.stderr b/src/test/ui/const-generics/invalid-enum.stderr
new file mode 100644 (file)
index 0000000..965abbc
--- /dev/null
@@ -0,0 +1,99 @@
+error[E0573]: expected type, found variant `CompileFlag::A`
+  --> $DIR/invalid-enum.rs:21:12
+   |
+LL |   test_1::<CompileFlag::A>();
+   |            ^^^^^^^^^^^^^^
+   |            |
+   |            not a type
+   |            help: try using the variant's enum: `CompileFlag`
+
+error[E0573]: expected type, found variant `CompileFlag::A`
+  --> $DIR/invalid-enum.rs:26:15
+   |
+LL |   test_2::<_, CompileFlag::A>(0);
+   |               ^^^^^^^^^^^^^^
+   |               |
+   |               not a type
+   |               help: try using the variant's enum: `CompileFlag`
+
+error[E0573]: expected type, found variant `CompileFlag::A`
+  --> $DIR/invalid-enum.rs:31:18
+   |
+LL |   let _: Example<CompileFlag::A, _> = Example { x: 0 };
+   |                  ^^^^^^^^^^^^^^
+   |                  |
+   |                  not a type
+   |                  help: try using the variant's enum: `CompileFlag`
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+  --> $DIR/invalid-enum.rs:31:10
+   |
+LL |   let _: Example<CompileFlag::A, _> = Example { x: 0 };
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected at most 1, found 2
+  --> $DIR/invalid-enum.rs:31:10
+   |
+LL |   let _: Example<CompileFlag::A, _> = Example { x: 0 };
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument
+   |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+   |
+LL |   let _: Example<{ CompileFlag::A }, _> = Example { x: 0 };
+   |                  ^                ^
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+  --> $DIR/invalid-enum.rs:36:10
+   |
+LL |   let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected at most 1, found 2
+  --> $DIR/invalid-enum.rs:36:10
+   |
+LL |   let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument
+   |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+   |
+LL |   let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 };
+   |                  ^                     ^
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+  --> $DIR/invalid-enum.rs:21:3
+   |
+LL |   test_1::<CompileFlag::A>();
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected 0, found 1
+  --> $DIR/invalid-enum.rs:21:12
+   |
+LL |   test_1::<CompileFlag::A>();
+   |            ^^^^^^^^^^^^^^ unexpected type argument
+   |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+   |
+LL |   test_1::<{ CompileFlag::A }>();
+   |            ^                ^
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+  --> $DIR/invalid-enum.rs:26:3
+   |
+LL |   test_2::<_, CompileFlag::A>(0);
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected 1, found 2
+  --> $DIR/invalid-enum.rs:26:15
+   |
+LL |   test_2::<_, CompileFlag::A>(0);
+   |               ^^^^^^^^^^^^^^ unexpected type argument
+   |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+   |
+LL |   test_2::<_, { CompileFlag::A }>(0);
+   |               ^                ^
+
+error: aborting due to 11 previous errors
+
+Some errors have detailed explanations: E0107, E0573.
+For more information about an error, try `rustc --explain E0107`.
index c8b3939933126996195ac32316a90a129032e295..bc0969e4f1fbadfb9454ab2b0c6855be6b94b5f9 100644 (file)
@@ -3,6 +3,7 @@
 #![feature(const_panic)]
 #![feature(duration_consts_2)]
 #![feature(div_duration)]
+#![feature(duration_saturating_ops)]
 
 use std::time::Duration;
 
@@ -15,29 +16,29 @@ fn duration() {
 
     const MAX : Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);
 
-    const MAX_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
-    assert_eq!(MAX_ADD_ZERO, Some(MAX));
+    const MAX_CHECKED_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
+    assert_eq!(MAX_CHECKED_ADD_ZERO, Some(MAX));
 
-    const MAX_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
-    assert_eq!(MAX_ADD_ONE, None);
+    const MAX_CHECKED_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
+    assert_eq!(MAX_CHECKED_ADD_ONE, None);
 
-    const ONE_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
-    assert_eq!(ONE_SUB_ONE, Some(ZERO));
+    const ONE_CHECKED_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
+    assert_eq!(ONE_CHECKED_SUB_ONE, Some(ZERO));
 
-    const ZERO_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
-    assert_eq!(ZERO_SUB_ONE, None);
+    const ZERO_CHECKED_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
+    assert_eq!(ZERO_CHECKED_SUB_ONE, None);
 
-    const ONE_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
-    assert_eq!(ONE_MUL_ONE, Some(ONE));
+    const ONE_CHECKED_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
+    assert_eq!(ONE_CHECKED_MUL_ONE, Some(ONE));
 
-    const MAX_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
-    assert_eq!(MAX_MUL_TWO, None);
+    const MAX_CHECKED_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
+    assert_eq!(MAX_CHECKED_MUL_TWO, None);
 
-    const ONE_DIV_ONE : Option<Duration> = ONE.checked_div(1);
-    assert_eq!(ONE_DIV_ONE, Some(ONE));
+    const ONE_CHECKED_DIV_ONE : Option<Duration> = ONE.checked_div(1);
+    assert_eq!(ONE_CHECKED_DIV_ONE, Some(ONE));
 
-    const ONE_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
-    assert_eq!(ONE_DIV_ZERO, None);
+    const ONE_CHECKED_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
+    assert_eq!(ONE_CHECKED_DIV_ZERO, None);
 
     const MAX_AS_F32 : f32 = MAX.as_secs_f32();
     assert_eq!(MAX_AS_F32, 18446744000000000000.0_f32);
@@ -50,6 +51,15 @@ fn duration() {
 
     const ONE_AS_F64 : f64 = ONE.div_duration_f64(ONE);
     assert_eq!(ONE_AS_F64, 1.0_f64);
+
+    const MAX_SATURATING_ADD_ONE : Duration = MAX.saturating_add(ONE);
+    assert_eq!(MAX_SATURATING_ADD_ONE, MAX);
+
+    const ZERO_SATURATING_SUB_ONE : Duration = ZERO.saturating_sub(ONE);
+    assert_eq!(ZERO_SATURATING_SUB_ONE, ZERO);
+
+    const MAX_SATURATING_MUL_TWO : Duration = MAX.saturating_mul(2);
+    assert_eq!(MAX_SATURATING_MUL_TWO, MAX);
 }
 
 fn main() {
diff --git a/src/test/ui/issues/auxiliary/issue-75907.rs b/src/test/ui/issues/auxiliary/issue-75907.rs
new file mode 100644 (file)
index 0000000..0b70452
--- /dev/null
@@ -0,0 +1,5 @@
+pub struct Bar(pub u8, u8, u8);
+
+pub fn make_bar() -> Bar {
+    Bar(1, 12, 10)
+}
diff --git a/src/test/ui/issues/issue-75907.rs b/src/test/ui/issues/issue-75907.rs
new file mode 100644 (file)
index 0000000..8c155d9
--- /dev/null
@@ -0,0 +1,18 @@
+// Test for for diagnostic improvement issue #75907
+
+mod foo {
+    pub(crate) struct Foo(u8);
+    pub(crate) struct Bar(pub u8, u8, Foo);
+
+    pub(crate) fn make_bar() -> Bar {
+        Bar(1, 12, Foo(10))
+    }
+}
+
+use foo::{make_bar, Bar, Foo};
+
+fn main() {
+    let Bar(x, y, Foo(z)) = make_bar();
+    //~^ ERROR expected tuple struct
+    //~| ERROR expected tuple struct
+}
diff --git a/src/test/ui/issues/issue-75907.stderr b/src/test/ui/issues/issue-75907.stderr
new file mode 100644 (file)
index 0000000..65b9a51
--- /dev/null
@@ -0,0 +1,29 @@
+error[E0532]: expected tuple struct or tuple variant, found struct `Bar`
+  --> $DIR/issue-75907.rs:15:9
+   |
+LL |     let Bar(x, y, Foo(z)) = make_bar();
+   |         ^^^
+   |
+note: constructor is not visible here due to private fields
+  --> $DIR/issue-75907.rs:15:16
+   |
+LL |     let Bar(x, y, Foo(z)) = make_bar();
+   |                ^  ^^^^^^ private field
+   |                |
+   |                private field
+
+error[E0532]: expected tuple struct or tuple variant, found struct `Foo`
+  --> $DIR/issue-75907.rs:15:19
+   |
+LL |     let Bar(x, y, Foo(z)) = make_bar();
+   |                   ^^^
+   |
+note: constructor is not visible here due to private fields
+  --> $DIR/issue-75907.rs:15:23
+   |
+LL |     let Bar(x, y, Foo(z)) = make_bar();
+   |                       ^ private field
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0532`.
diff --git a/src/test/ui/issues/issue-75907_b.rs b/src/test/ui/issues/issue-75907_b.rs
new file mode 100644 (file)
index 0000000..fdd3bc6
--- /dev/null
@@ -0,0 +1,11 @@
+// Test for for diagnostic improvement issue #75907, extern crate
+// aux-build:issue-75907.rs
+
+extern crate issue_75907 as a;
+
+use a::{make_bar, Bar};
+
+fn main() {
+    let Bar(x, y, z) = make_bar();
+    //~^ ERROR expected tuple struct
+}
diff --git a/src/test/ui/issues/issue-75907_b.stderr b/src/test/ui/issues/issue-75907_b.stderr
new file mode 100644 (file)
index 0000000..cdd21de
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0532]: expected tuple struct or tuple variant, found struct `Bar`
+  --> $DIR/issue-75907_b.rs:9:9
+   |
+LL |     let Bar(x, y, z) = make_bar();
+   |         ^^^ constructor is not visible here due to private fields
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0532`.