Skip to content

Commit 8d67a66

Browse files
committed
Auto merge of #64564 - jonas-schievink:cowardly-default, r=<try>
Deny specializing items not in the parent impl Part of #29661 (rust-lang/rfcs#2532). At least sort of? This was discussed in #61812 (comment) and is needed for that PR to make progress (fixing an unsoundness). One annoyance with doing this is that it sometimes requires users to copy-paste a provided trait method into an impl just to mark it `default` (ie. there is no syntax to forward this impl method to the provided trait method). cc @Centril and @arielb1
2 parents 9b9d2af + 88f3075 commit 8d67a66

File tree

12 files changed

+268
-45
lines changed

12 files changed

+268
-45
lines changed

src/liballoc/boxed.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -883,11 +883,33 @@ impl<I: Iterator + ?Sized> Iterator for Box<I> {
883883
fn nth(&mut self, n: usize) -> Option<I::Item> {
884884
(**self).nth(n)
885885
}
886+
fn last(self) -> Option<I::Item> {
887+
BoxIter::last(self)
888+
}
889+
}
890+
891+
trait BoxIter {
892+
type Item;
893+
fn last(self) -> Option<Self::Item>;
894+
}
895+
896+
impl<I: Iterator + ?Sized> BoxIter for Box<I> {
897+
type Item = I::Item;
898+
default fn last(self) -> Option<I::Item> {
899+
#[inline]
900+
fn some<T>(_: Option<T>, x: T) -> Option<T> {
901+
Some(x)
902+
}
903+
904+
self.fold(None, some)
905+
}
886906
}
887907

908+
/// Specialization for sized `I`s that uses `I`s implementation of `last()`
909+
/// instead of the default.
888910
#[stable(feature = "rust1", since = "1.0.0")]
889-
impl<I: Iterator + Sized> Iterator for Box<I> {
890-
fn last(self) -> Option<I::Item> where I: Sized {
911+
impl<I: Iterator> BoxIter for Box<I> {
912+
fn last(self) -> Option<I::Item> {
891913
(*self).last()
892914
}
893915
}

src/librustc/traits/project.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1504,8 +1504,8 @@ fn assoc_ty_def(
15041504

15051505
if let Some(assoc_item) = trait_def
15061506
.ancestors(tcx, impl_def_id)
1507-
.defs(tcx, assoc_ty_name, ty::AssocKind::Type, trait_def_id)
1508-
.next() {
1507+
.leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) {
1508+
15091509
assoc_item
15101510
} else {
15111511
// This is saying that neither the trait nor

src/librustc/traits/specialize/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub fn find_associated_item<'tcx>(
125125
let trait_def = tcx.trait_def(trait_def_id);
126126

127127
let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id);
128-
match ancestors.defs(tcx, item.ident, item.kind, trait_def_id).next() {
128+
match ancestors.leaf_def(tcx, item.ident, item.kind) {
129129
Some(node_item) => {
130130
let substs = tcx.infer_ctxt().enter(|infcx| {
131131
let param_env = param_env.with_reveal_all();

src/librustc/traits/specialize/specialization_graph.rs

+39-24
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::traits;
88
use crate::ty::{self, TyCtxt, TypeFoldable};
99
use crate::ty::fast_reject::{self, SimplifiedType};
1010
use syntax::ast::Ident;
11-
use crate::util::captures::Captures;
1211
use crate::util::nodemap::{DefIdMap, FxHashMap};
1312

1413
/// A per-trait graph of impls in specialization order. At the moment, this
@@ -421,6 +420,35 @@ impl<'tcx> Node {
421420
tcx.associated_items(self.def_id())
422421
}
423422

423+
/// Finds an associated item defined in this node.
424+
///
425+
/// If this returns `None`, the item can potentially still be found in
426+
/// parents of this node.
427+
pub fn item(
428+
&self,
429+
tcx: TyCtxt<'tcx>,
430+
trait_item_name: Ident,
431+
trait_item_kind: ty::AssocKind,
432+
trait_def_id: DefId,
433+
) -> Option<ty::AssocItem> {
434+
use crate::ty::AssocKind::*;
435+
436+
tcx.associated_items(self.def_id())
437+
.find(move |impl_item| match (trait_item_kind, impl_item.kind) {
438+
| (Const, Const)
439+
| (Method, Method)
440+
| (Type, Type)
441+
| (Type, OpaqueTy) // assoc. types can be made opaque in impls
442+
=> tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id),
443+
444+
| (Const, _)
445+
| (Method, _)
446+
| (Type, _)
447+
| (OpaqueTy, _)
448+
=> false,
449+
})
450+
}
451+
424452
pub fn def_id(&self) -> DefId {
425453
match *self {
426454
Node::Impl(did) => did,
@@ -429,6 +457,7 @@ impl<'tcx> Node {
429457
}
430458
}
431459

460+
#[derive(Copy, Clone)]
432461
pub struct Ancestors<'tcx> {
433462
trait_def_id: DefId,
434463
specialization_graph: &'tcx Graph,
@@ -467,32 +496,18 @@ impl<T> NodeItem<T> {
467496
}
468497

469498
impl<'tcx> Ancestors<'tcx> {
470-
/// Search the items from the given ancestors, returning each definition
471-
/// with the given name and the given kind.
472-
// FIXME(#35870): avoid closures being unexported due to `impl Trait`.
473-
#[inline]
474-
pub fn defs(
475-
self,
499+
/// Finds the bottom-most (ie. most specialized) definition of an associated
500+
/// item.
501+
pub fn leaf_def(
502+
mut self,
476503
tcx: TyCtxt<'tcx>,
477504
trait_item_name: Ident,
478505
trait_item_kind: ty::AssocKind,
479-
trait_def_id: DefId,
480-
) -> impl Iterator<Item = NodeItem<ty::AssocItem>> + Captures<'tcx> + 'tcx {
481-
self.flat_map(move |node| {
482-
use crate::ty::AssocKind::*;
483-
node.items(tcx).filter(move |impl_item| match (trait_item_kind, impl_item.kind) {
484-
| (Const, Const)
485-
| (Method, Method)
486-
| (Type, Type)
487-
| (Type, OpaqueTy)
488-
=> tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id),
489-
490-
| (Const, _)
491-
| (Method, _)
492-
| (Type, _)
493-
| (OpaqueTy, _)
494-
=> false,
495-
}).map(move |item| NodeItem { node: node, item: item })
506+
) -> Option<NodeItem<ty::AssocItem>> {
507+
let trait_def_id = self.trait_def_id;
508+
self.find_map(|node| {
509+
node.item(tcx, trait_item_name, trait_item_kind, trait_def_id)
510+
.map(|item| NodeItem { node, item })
496511
})
497512
}
498513
}

src/librustc/traits/util.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use syntax_pos::Span;
44

55
use crate::hir;
66
use crate::hir::def_id::DefId;
7-
use crate::traits::specialize::specialization_graph::NodeItem;
87
use crate::ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef};
98
use crate::ty::outlives::Component;
109
use crate::ty::subst::{Kind, Subst, SubstsRef};
@@ -669,8 +668,8 @@ impl<'tcx> TyCtxt<'tcx> {
669668
}
670669
}
671670

672-
pub fn impl_item_is_final(self, node_item: &NodeItem<hir::Defaultness>) -> bool {
673-
node_item.item.is_final() && !self.impl_is_default(node_item.node.def_id())
671+
pub fn impl_item_is_final(self, assoc_item: &ty::AssocItem) -> bool {
672+
assoc_item.defaultness.is_final() && !self.impl_is_default(assoc_item.container.id())
674673
}
675674
}
676675

src/librustc/ty/query/config.rs

+11
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ impl<'tcx, M: QueryAccessors<'tcx, Key = DefId>> QueryDescription<'tcx> for M {
7373
format!("processing {:?} with query `{}`", def_id, name).into()
7474
}
7575
}
76+
77+
default fn cache_on_disk(_: TyCtxt<'tcx>, _: Self::Key, _: Option<&Self::Value>) -> bool {
78+
false
79+
}
80+
81+
default fn try_load_from_disk(
82+
_: TyCtxt<'tcx>,
83+
_: SerializedDepNodeIndex,
84+
) -> Option<Self::Value> {
85+
bug!("QueryDescription::load_from_disk() called for an unsupported query.")
86+
}
7687
}
7788

7889
impl<'tcx> QueryDescription<'tcx> for queries::analysis<'tcx> {

src/librustc_typeck/check/mod.rs

+45-10
Original file line numberDiff line numberDiff line change
@@ -1650,24 +1650,60 @@ fn check_specialization_validity<'tcx>(
16501650
impl_id: DefId,
16511651
impl_item: &hir::ImplItem,
16521652
) {
1653-
let ancestors = trait_def.ancestors(tcx, impl_id);
1654-
16551653
let kind = match impl_item.node {
16561654
hir::ImplItemKind::Const(..) => ty::AssocKind::Const,
16571655
hir::ImplItemKind::Method(..) => ty::AssocKind::Method,
16581656
hir::ImplItemKind::OpaqueTy(..) => ty::AssocKind::OpaqueTy,
16591657
hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type,
16601658
};
16611659

1662-
let parent = ancestors.defs(tcx, trait_item.ident, kind, trait_def.def_id).nth(1)
1663-
.map(|node_item| node_item.map(|parent| parent.defaultness));
1660+
let mut ancestor_impls = trait_def.ancestors(tcx, impl_id)
1661+
.skip(1)
1662+
.filter_map(|parent| {
1663+
if parent.is_from_trait() {
1664+
None
1665+
} else {
1666+
Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id)))
1667+
}
1668+
})
1669+
.peekable();
16641670

1665-
if let Some(parent) = parent {
1666-
if tcx.impl_item_is_final(&parent) {
1667-
report_forbidden_specialization(tcx, impl_item, parent.node.def_id());
1668-
}
1671+
if ancestor_impls.peek().is_none() {
1672+
// No parent, nothing to specialize.
1673+
return;
16691674
}
16701675

1676+
let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| {
1677+
match parent_item {
1678+
// Parent impl exists, and contains the parent item we're trying to specialize, but
1679+
// doesn't mark it `default`.
1680+
Some(parent_item) if tcx.impl_item_is_final(&parent_item) => {
1681+
Some(Err(parent_impl.def_id()))
1682+
}
1683+
1684+
// Parent impl contains item and makes it specializable.
1685+
Some(_) => {
1686+
Some(Ok(()))
1687+
}
1688+
1689+
// Parent impl doesn't mention the item. This means it's inherited from the
1690+
// grandparent. In that case, if parent is a `default impl`, inherited items use the
1691+
// "defaultness" from the grandparent, else they are final.
1692+
None => if tcx.impl_is_default(parent_impl.def_id()) {
1693+
None
1694+
} else {
1695+
Some(Err(parent_impl.def_id()))
1696+
}
1697+
}
1698+
});
1699+
1700+
// If `opt_result` is `None`, we have only encoutered `default impl`s that don't contain the
1701+
// item. This is allowed, the item isn't actually getting specialized here.
1702+
let result = opt_result.unwrap_or(Ok(()));
1703+
1704+
if let Err(parent_impl) = result {
1705+
report_forbidden_specialization(tcx, impl_item, parent_impl);
1706+
}
16711707
}
16721708

16731709
fn check_impl_items_against_trait<'tcx>(
@@ -1783,8 +1819,7 @@ fn check_impl_items_against_trait<'tcx>(
17831819
let associated_type_overridden = overridden_associated_type.is_some();
17841820
for trait_item in tcx.associated_items(impl_trait_ref.def_id) {
17851821
let is_implemented = trait_def.ancestors(tcx, impl_id)
1786-
.defs(tcx, trait_item.ident, trait_item.kind, impl_trait_ref.def_id)
1787-
.next()
1822+
.leaf_def(tcx, trait_item.ident, trait_item.kind)
17881823
.map(|node_item| !node_item.node.is_from_trait())
17891824
.unwrap_or(false);
17901825

src/test/ui/specialization/auxiliary/cross_crates_defaults.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ pub trait Bar {
2222
fn bar(&self) -> i32 { 0 }
2323
}
2424

25-
impl<T> Bar for T {} // use the provided method
25+
impl<T> Bar for T {
26+
default fn bar(&self) -> i32 { 0 }
27+
}
2628

2729
impl Bar for i32 {
2830
fn bar(&self) -> i32 { 1 }

src/test/ui/specialization/issue-36804.rs

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ where
1313
fn next(&mut self) -> Option<T> {
1414
unimplemented!()
1515
}
16+
17+
default fn count(self) -> usize where Self: Sized {
18+
self.fold(0, |cnt, _| cnt + 1)
19+
}
1620
}
1721

1822
impl<'a, I, T: 'a> Iterator for Cloned<I>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#![feature(specialization, associated_type_defaults)]
2+
3+
// Test that attempting to override a non-default method or one not in the
4+
// parent impl causes an error.
5+
6+
trait Foo {
7+
type Ty = ();
8+
const CONST: u8 = 123;
9+
fn foo(&self) -> bool { true }
10+
}
11+
12+
// Specialization tree for Foo:
13+
//
14+
// Box<T> Vec<T>
15+
// / \ / \
16+
// Box<i32> Box<i64> Vec<()> Vec<bool>
17+
18+
impl<T> Foo for Box<T> {
19+
type Ty = bool;
20+
const CONST: u8 = 0;
21+
fn foo(&self) -> bool { false }
22+
}
23+
24+
// Allowed
25+
impl Foo for Box<i32> {}
26+
27+
// Can't override a non-`default` fn
28+
impl Foo for Box<i64> {
29+
type Ty = Vec<()>;
30+
//~^ error: `Ty` specializes an item from a parent `impl`, but that item is not marked `default`
31+
const CONST: u8 = 42;
32+
//~^ error: `CONST` specializes an item from a parent `impl`, but that item is not marked `default`
33+
fn foo(&self) -> bool { true }
34+
//~^ error: `foo` specializes an item from a parent `impl`, but that item is not marked `default`
35+
}
36+
37+
38+
// Doesn't mention the item = provided body/value is used and the method is final.
39+
impl<T> Foo for Vec<T> {}
40+
41+
// Allowed
42+
impl Foo for Vec<()> {}
43+
44+
impl Foo for Vec<bool> {
45+
type Ty = Vec<()>;
46+
//~^ error: `Ty` specializes an item from a parent `impl`, but that item is not marked `default`
47+
const CONST: u8 = 42;
48+
//~^ error: `CONST` specializes an item from a parent `impl`, but that item is not marked `default`
49+
fn foo(&self) -> bool { true }
50+
//~^ error: `foo` specializes an item from a parent `impl`, but that item is not marked `default`
51+
}
52+
53+
fn main() {}

0 commit comments

Comments
 (0)