Skip to content

Commit 190adc0

Browse files
committed
Properly check traits in type privacy
1 parent 53779ed commit 190adc0

File tree

7 files changed

+155
-93
lines changed

7 files changed

+155
-93
lines changed

src/librustc/hir/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,13 @@ impl Path {
258258

259259
impl fmt::Debug for Path {
260260
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261-
write!(f, "path({})",
262-
print::to_string(print::NO_ANN, |s| s.print_path(self, false)))
261+
write!(f, "path({})", print::to_string(print::NO_ANN, |s| s.print_path(self, false)))
262+
}
263+
}
264+
265+
impl fmt::Display for Path {
266+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
267+
write!(f, "{}", print::to_string(print::NO_ANN, |s| s.print_path(self, false)))
263268
}
264269
}
265270

src/librustc_privacy/lib.rs

+59-29
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ struct TypePrivacyVisitor<'a, 'tcx: 'a> {
614614
tcx: TyCtxt<'a, 'tcx, 'tcx>,
615615
tables: &'a ty::TypeckTables<'tcx>,
616616
current_item: DefId,
617+
in_body: bool,
617618
span: Span,
618619
empty_tables: &'a ty::TypeckTables<'tcx>,
619620
}
@@ -671,10 +672,8 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
671672
// Take node ID of an expression or pattern and check its type for privacy.
672673
fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool {
673674
self.span = span;
674-
if let Some(ty) = self.tables.node_id_to_type_opt(id) {
675-
if ty.visit_with(self) {
676-
return true;
677-
}
675+
if self.tables.node_id_to_type(id).visit_with(self) {
676+
return true;
678677
}
679678
if self.tables.node_substs(id).visit_with(self) {
680679
return true;
@@ -688,6 +687,16 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
688687
}
689688
false
690689
}
690+
691+
fn check_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool {
692+
if !self.item_is_accessible(trait_ref.def_id) {
693+
let msg = format!("trait `{}` is private", trait_ref);
694+
self.tcx.sess.span_err(self.span, &msg);
695+
return true;
696+
}
697+
698+
trait_ref.super_visit_with(self)
699+
}
691700
}
692701

693702
impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
@@ -699,16 +708,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
699708

700709
fn visit_nested_body(&mut self, body: hir::BodyId) {
701710
let orig_tables = replace(&mut self.tables, self.tcx.body_tables(body));
711+
let orig_in_body = replace(&mut self.in_body, true);
702712
let body = self.tcx.hir.body(body);
703713
self.visit_body(body);
704714
self.tables = orig_tables;
715+
self.in_body = orig_in_body;
705716
}
706717

707718
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty) {
708719
self.span = hir_ty.span;
709-
if let Some(ty) = self.tables.node_id_to_type_opt(hir_ty.hir_id) {
720+
if self.in_body {
710721
// Types in bodies.
711-
if ty.visit_with(self) {
722+
if self.tables.node_id_to_type(hir_ty.hir_id).visit_with(self) {
712723
return;
713724
}
714725
} else {
@@ -724,10 +735,21 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
724735
}
725736

726737
fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef) {
727-
if !self.item_is_accessible(trait_ref.path.def.def_id()) {
728-
let msg = format!("trait `{:?}` is private", trait_ref.path);
729-
self.tcx.sess.span_err(self.span, &msg);
730-
return;
738+
self.span = trait_ref.path.span;
739+
if !self.in_body {
740+
// Avoid calling `hir_trait_to_predicates` in bodies, it will ICE.
741+
// The traits' privacy in bodies is already checked as a part of trait object types.
742+
let (principal, projections) =
743+
rustc_typeck::hir_trait_to_predicates(self.tcx, trait_ref);
744+
if self.check_trait_ref(*principal.skip_binder()) {
745+
return;
746+
}
747+
for poly_predicate in projections {
748+
let tcx = self.tcx;
749+
if self.check_trait_ref(poly_predicate.skip_binder().projection_ty.trait_ref(tcx)) {
750+
return;
751+
}
752+
}
731753
}
732754

733755
intravisit::walk_trait_ref(self, trait_ref);
@@ -760,19 +782,29 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
760782
intravisit::walk_expr(self, expr);
761783
}
762784

785+
// Prohibit access to associated items with insufficient nominal visibility.
763786
fn visit_qpath(&mut self, qpath: &'tcx hir::QPath, id: ast::NodeId, span: Span) {
764-
// Inherent associated constants don't have self type in substs,
765-
// we have to check it additionally.
766-
if let hir::QPath::TypeRelative(..) = *qpath {
767-
let hir_id = self.tcx.hir.node_to_hir_id(id);
768-
if let Some(def) = self.tables.type_dependent_defs().get(hir_id).cloned() {
769-
if let Some(assoc_item) = self.tcx.opt_associated_item(def.def_id()) {
770-
if let ty::ImplContainer(impl_def_id) = assoc_item.container {
771-
if self.tcx.type_of(impl_def_id).visit_with(self) {
772-
return;
773-
}
774-
}
775-
}
787+
let def = match *qpath {
788+
hir::QPath::Resolved(_, ref path) => match path.def {
789+
Def::Method(..) | Def::AssociatedConst(..) |
790+
Def::AssociatedTy(..) => Some(path.def),
791+
_ => None,
792+
}
793+
hir::QPath::TypeRelative(..) => {
794+
let hir_id = self.tcx.hir.node_to_hir_id(id);
795+
self.tables.type_dependent_defs().get(hir_id).cloned()
796+
}
797+
};
798+
if let Some(def) = def {
799+
let def_id = def.def_id();
800+
if !self.item_is_accessible(def_id) {
801+
let name = match *qpath {
802+
hir::QPath::Resolved(_, ref path) => format!("{}", path),
803+
hir::QPath::TypeRelative(_, ref segment) => segment.name.to_string(),
804+
};
805+
let msg = format!("{} `{}` is private", def.kind_name(), name);
806+
self.tcx.sess.span_err(span, &msg);
807+
return;
776808
}
777809
}
778810

@@ -807,9 +839,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
807839
item.id,
808840
&mut self.tables,
809841
self.empty_tables);
842+
let orig_in_body = replace(&mut self.in_body, false);
810843
self.current_item = self.tcx.hir.local_def_id(item.id);
811844
intravisit::walk_item(self, item);
812845
self.tables = orig_tables;
846+
self.in_body = orig_in_body;
813847
self.current_item = orig_current_item;
814848
}
815849

@@ -869,13 +903,8 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
869903
}
870904
}
871905
ty::TyProjection(ref proj) => {
872-
let trait_ref = proj.trait_ref(self.tcx);
873-
if !self.item_is_accessible(trait_ref.def_id) {
874-
let msg = format!("trait `{}` is private", trait_ref);
875-
self.tcx.sess.span_err(self.span, &msg);
876-
return true;
877-
}
878-
if trait_ref.super_visit_with(self) {
906+
let tcx = self.tcx;
907+
if self.check_trait_ref(proj.trait_ref(tcx)) {
879908
return true;
880909
}
881910
}
@@ -1629,6 +1658,7 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
16291658
tcx,
16301659
tables: &empty_tables,
16311660
current_item: DefId::local(CRATE_DEF_INDEX),
1661+
in_body: false,
16321662
span: krate.span,
16331663
empty_tables: &empty_tables,
16341664
};

src/librustc_typeck/astconv.rs

+64-50
Original file line numberDiff line numberDiff line change
@@ -347,13 +347,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
347347
}
348348
}
349349

350-
pub fn instantiate_poly_trait_ref(&self,
351-
ast_trait_ref: &hir::PolyTraitRef,
350+
pub(super) fn instantiate_poly_trait_ref_inner(&self,
351+
trait_ref: &hir::TraitRef,
352352
self_ty: Ty<'tcx>,
353-
poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
353+
poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>,
354+
speculative: bool)
354355
-> ty::PolyTraitRef<'tcx>
355356
{
356-
let trait_ref = &ast_trait_ref.trait_ref;
357357
let trait_def_id = self.trait_def_id(trait_ref);
358358

359359
debug!("ast_path_to_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id);
@@ -371,7 +371,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
371371
// specify type to assert that error was already reported in Err case:
372372
let predicate: Result<_, ErrorReported> =
373373
self.ast_type_binding_to_poly_projection_predicate(trait_ref.ref_id, poly_trait_ref,
374-
binding);
374+
binding, speculative);
375375
predicate.ok() // ok to ignore Err() because ErrorReported (see above)
376376
}));
377377

@@ -380,6 +380,16 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
380380
poly_trait_ref
381381
}
382382

383+
pub fn instantiate_poly_trait_ref(&self,
384+
poly_trait_ref: &hir::PolyTraitRef,
385+
self_ty: Ty<'tcx>,
386+
poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
387+
-> ty::PolyTraitRef<'tcx>
388+
{
389+
self.instantiate_poly_trait_ref_inner(&poly_trait_ref.trait_ref, self_ty,
390+
poly_projections, false)
391+
}
392+
383393
fn ast_path_to_mono_trait_ref(&self,
384394
span: Span,
385395
trait_def_id: DefId,
@@ -445,55 +455,59 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
445455
&self,
446456
ref_id: ast::NodeId,
447457
trait_ref: ty::PolyTraitRef<'tcx>,
448-
binding: &ConvertedBinding<'tcx>)
458+
binding: &ConvertedBinding<'tcx>,
459+
speculative: bool)
449460
-> Result<ty::PolyProjectionPredicate<'tcx>, ErrorReported>
450461
{
451462
let tcx = self.tcx();
452463

453-
// Given something like `U : SomeTrait<T=X>`, we want to produce a
454-
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
455-
// subtle in the event that `T` is defined in a supertrait of
456-
// `SomeTrait`, because in that case we need to upcast.
457-
//
458-
// That is, consider this case:
459-
//
460-
// ```
461-
// trait SubTrait : SuperTrait<int> { }
462-
// trait SuperTrait<A> { type T; }
463-
//
464-
// ... B : SubTrait<T=foo> ...
465-
// ```
466-
//
467-
// We want to produce `<B as SuperTrait<int>>::T == foo`.
468-
469-
// Find any late-bound regions declared in `ty` that are not
470-
// declared in the trait-ref. These are not wellformed.
471-
//
472-
// Example:
473-
//
474-
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
475-
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
476-
let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&trait_ref);
477-
let late_bound_in_ty = tcx.collect_referenced_late_bound_regions(&ty::Binder(binding.ty));
478-
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
479-
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
480-
for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
481-
let br_name = match *br {
482-
ty::BrNamed(_, name) => name,
483-
_ => {
484-
span_bug!(
485-
binding.span,
486-
"anonymous bound region {:?} in binding but not trait ref",
487-
br);
488-
}
489-
};
490-
struct_span_err!(tcx.sess,
491-
binding.span,
492-
E0582,
493-
"binding for associated type `{}` references lifetime `{}`, \
494-
which does not appear in the trait input types",
495-
binding.item_name, br_name)
496-
.emit();
464+
if !speculative {
465+
// Given something like `U : SomeTrait<T=X>`, we want to produce a
466+
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
467+
// subtle in the event that `T` is defined in a supertrait of
468+
// `SomeTrait`, because in that case we need to upcast.
469+
//
470+
// That is, consider this case:
471+
//
472+
// ```
473+
// trait SubTrait : SuperTrait<int> { }
474+
// trait SuperTrait<A> { type T; }
475+
//
476+
// ... B : SubTrait<T=foo> ...
477+
// ```
478+
//
479+
// We want to produce `<B as SuperTrait<int>>::T == foo`.
480+
481+
// Find any late-bound regions declared in `ty` that are not
482+
// declared in the trait-ref. These are not wellformed.
483+
//
484+
// Example:
485+
//
486+
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
487+
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
488+
let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&trait_ref);
489+
let late_bound_in_ty =
490+
tcx.collect_referenced_late_bound_regions(&ty::Binder(binding.ty));
491+
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
492+
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
493+
for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
494+
let br_name = match *br {
495+
ty::BrNamed(_, name) => name,
496+
_ => {
497+
span_bug!(
498+
binding.span,
499+
"anonymous bound region {:?} in binding but not trait ref",
500+
br);
501+
}
502+
};
503+
struct_span_err!(tcx.sess,
504+
binding.span,
505+
E0582,
506+
"binding for associated type `{}` references lifetime `{}`, \
507+
which does not appear in the trait input types",
508+
binding.item_name, br_name)
509+
.emit();
510+
}
497511
}
498512

499513
let candidate = if self.trait_defines_associated_type_named(trait_ref.def_id(),

src/librustc_typeck/lib.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,22 @@ pub fn hir_ty_to_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir_ty: &hir::Ty) ->
348348
let env_node_id = tcx.hir.get_parent(hir_ty.id);
349349
let env_def_id = tcx.hir.local_def_id(env_node_id);
350350
let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id);
351-
item_cx.to_ty(hir_ty)
351+
astconv::AstConv::ast_ty_to_ty(&item_cx, hir_ty)
352+
}
353+
354+
pub fn hir_trait_to_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir_trait: &hir::TraitRef)
355+
-> (ty::PolyTraitRef<'tcx>, Vec<ty::PolyProjectionPredicate<'tcx>>) {
356+
// In case there are any projections etc, find the "environment"
357+
// def-id that will be used to determine the traits/predicates in
358+
// scope. This is derived from the enclosing item-like thing.
359+
let env_node_id = tcx.hir.get_parent(hir_trait.ref_id);
360+
let env_def_id = tcx.hir.local_def_id(env_node_id);
361+
let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id);
362+
let mut projections = Vec::new();
363+
let principal = astconv::AstConv::instantiate_poly_trait_ref_inner(
364+
&item_cx, hir_trait, tcx.types.err, &mut projections, true
365+
);
366+
(principal, projections)
352367
}
353368

354369
__build_diagnostic_array! { librustc_typeck, DIAGNOSTICS }

src/test/compile-fail/privacy/associated-item-privacy-inherent.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mod priv_nominal {
2727
Pub.method();
2828
//~^ ERROR type `for<'r> fn(&'r priv_nominal::Pub) {priv_nominal::Pub::method}` is private
2929
Pub::CONST;
30-
//FIXME ERROR associated constant `CONST` is private
30+
//~^ ERROR associated constant `CONST` is private
3131
// let _: Pub::AssocTy;
3232
// pub type InSignatureTy = Pub::AssocTy;
3333
}

src/test/compile-fail/privacy/associated-item-privacy-trait.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,16 @@ mod priv_trait {
3131
Pub.method();
3232
//~^ ERROR type `for<'r> fn(&'r Self) {<Self as priv_trait::PrivTr>::method}` is private
3333
<Pub as PrivTr>::CONST;
34-
//FIXME ERROR associated constant `path(PrivTr::CONST)` is private
34+
//~^ ERROR associated constant `PrivTr::CONST` is private
3535
let _: <Pub as PrivTr>::AssocTy;
3636
//~^ ERROR trait `priv_trait::PrivTr` is private
3737
//~| ERROR trait `priv_trait::PrivTr` is private
3838
pub type InSignatureTy = <Pub as PrivTr>::AssocTy;
3939
//~^ ERROR trait `priv_trait::PrivTr` is private
40-
//~| ERROR trait `path(PrivTr)` is private
4140
pub trait InSignatureTr: PrivTr {}
42-
//FIXME ERROR trait `priv_trait::PrivTr` is private
41+
//~^ ERROR trait `priv_trait::PrivTr` is private
4342
impl PrivTr for u8 {}
44-
//FIXME ERROR trait `priv_trait::PrivTr` is private
43+
//~^ ERROR trait `priv_trait::PrivTr` is private
4544
}
4645
}
4746
fn priv_trait() {
@@ -142,7 +141,7 @@ mod priv_parent_substs {
142141
pub type InSignatureTy2 = <Priv as PubTr<Pub>>::AssocTy;
143142
//~^ ERROR type `priv_parent_substs::Priv` is private
144143
impl PubTr for u8 {}
145-
//FIXME ERROR type `priv_parent_substs::Priv` is private
144+
//~^ ERROR type `priv_parent_substs::Priv` is private
146145
}
147146
}
148147
fn priv_parent_substs() {

0 commit comments

Comments
 (0)