Skip to content

Commit dfd6b8e

Browse files
committed
Lower impl restriction to rustc_middle
1 parent 74fa315 commit dfd6b8e

File tree

7 files changed

+227
-8
lines changed

7 files changed

+227
-8
lines changed

compiler/rustc_middle/src/hir/map.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1179,14 +1179,15 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh {
11791179
}
11801180
tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
11811181
tcx.stable_crate_id(LOCAL_CRATE).hash_stable(&mut hcx, &mut stable_hasher);
1182-
// Hash visibility information since it does not appear in HIR.
1182+
// Hash visibility and restriction information since it does not appear in HIR.
11831183
// FIXME: Figure out how to remove `visibilities_for_hashing` by hashing visibilities on
11841184
// the fly in the resolver, storing only their accumulated hash in `ResolverGlobalCtxt`,
11851185
// and combining it with other hashes here.
11861186
resolutions.visibilities_for_hashing.hash_stable(&mut hcx, &mut stable_hasher);
11871187
with_metavar_spans(|mspans| {
11881188
mspans.freeze_and_get_read_spans().hash_stable(&mut hcx, &mut stable_hasher);
11891189
});
1190+
resolutions.impl_restrictions.hash_stable(&mut hcx, &mut stable_hasher);
11901191
stable_hasher.finish()
11911192
});
11921193

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ pub struct ResolverOutputs {
178178
#[derive(Debug)]
179179
pub struct ResolverGlobalCtxt {
180180
pub visibilities_for_hashing: Vec<(LocalDefId, Visibility)>,
181+
pub impl_restrictions: FxIndexMap<LocalDefId, Restriction>,
181182
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
182183
pub expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
183184
pub effective_visibilities: EffectiveVisibilities,
@@ -322,6 +323,35 @@ impl Visibility {
322323
}
323324
}
324325

326+
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Encodable, Decodable, HashStable)]
327+
pub enum Restriction {
328+
/// The restriction does not affect the item.
329+
Unrestricted,
330+
/// The restriction only applies outside of this path.
331+
Restricted(DefId, Span),
332+
}
333+
334+
impl Restriction {
335+
/// Returns `true` if the behavior is allowed/unrestricted in the given module. A value of
336+
/// `false` indicates that the behavior is prohibited.
337+
pub fn is_allowed_in(self, module: DefId, tcx: TyCtxt<'_>) -> bool {
338+
let restricted_to = match self {
339+
Restriction::Unrestricted => return true,
340+
Restriction::Restricted(module, _) => module,
341+
};
342+
343+
tcx.is_descendant_of(module, restricted_to.into())
344+
}
345+
346+
/// Obtain the [`Span`] of the restriction. If unrestricted, an empty span is returned.
347+
pub fn span(&self) -> Span {
348+
match self {
349+
Restriction::Unrestricted => DUMMY_SP,
350+
Restriction::Restricted(_, span) => *span,
351+
}
352+
}
353+
}
354+
325355
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)]
326356
#[derive(TypeFoldable, TypeVisitable)]
327357
pub struct ClosureSizeProfileData<'tcx> {

compiler/rustc_resolve/messages.ftl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,16 @@ resolve_remove_surrounding_derive =
353353
354354
resolve_remove_unnecessary_import = remove unnecessary import
355355
356+
resolve_restriction_ancestor_only = restrictions can only be restricted to ancestor modules
357+
358+
resolve_restriction_indeterminate = cannot determine resolution for the restriction
359+
360+
resolve_restriction_module_only = restriction must resolve to a module
361+
362+
resolve_restriction_relative_2018 =
363+
relative paths are not supported in restrictions in 2018 edition or later
364+
.suggestion = try
365+
356366
resolve_self_import_can_only_appear_once_in_the_list =
357367
`self` import can only appear once in an import list
358368
.label = can only appear once in an import list

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
3333
use crate::{
3434
BindingKey, Determinacy, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind,
3535
ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult,
36-
ResolutionError, Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError,
37-
errors,
36+
ResolutionError, Resolver, ResolverArenas, RestrictionResolutionError, Segment, ToNameBinding,
37+
Used, VisResolutionError, errors,
3838
};
3939

4040
type Res = def::Res<NodeId>;
@@ -322,9 +322,9 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
322322
})
323323
}
324324
ast::VisibilityKind::Restricted { ref path, id, .. } => {
325-
// For visibilities we are not ready to provide correct implementation of "uniform
325+
// For restrictions we are not ready to provide correct implementation of "uniform
326326
// paths" right now, so on 2018 edition we only allow module-relative paths for now.
327-
// On 2015 edition visibilities are resolved as crate-relative by default,
327+
// On 2015 edition restrictions are resolved as crate-relative by default,
328328
// so we are prepending a root segment if necessary.
329329
let ident = path.segments.get(0).expect("empty path in visibility").ident;
330330
let crate_root = if ident.is_path_segment_keyword() {
@@ -406,6 +406,97 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
406406
self.r.field_names.insert(def_id, fields);
407407
}
408408

409+
fn resolve_restriction(&mut self, restriction: &ast::Restriction) -> ty::Restriction {
410+
self.try_resolve_restriction(restriction, true).unwrap_or_else(|err| {
411+
self.r.report_restriction_error(err);
412+
ty::Restriction::Unrestricted
413+
})
414+
}
415+
416+
fn try_resolve_restriction<'ast>(
417+
&mut self,
418+
restriction: &'ast ast::Restriction,
419+
finalize: bool,
420+
) -> Result<ty::Restriction, RestrictionResolutionError<'ast>> {
421+
let parent_scope = &self.parent_scope;
422+
match restriction.kind {
423+
// If the restriction is implied, it has no effect when the item is otherwise visible.
424+
ast::RestrictionKind::Unrestricted | ast::RestrictionKind::Implied => {
425+
Ok(ty::Restriction::Unrestricted)
426+
}
427+
ast::RestrictionKind::Restricted { ref path, id, shorthand: _ } => {
428+
// For restrictions we are not ready to provide correct implementation of "uniform
429+
// paths" right now, so on 2018 edition we only allow module-relative paths for now.
430+
// On 2015 edition visibilities are resolved as crate-relative by default,
431+
// so we are prepending a root segment if necessary.
432+
let ident = path.segments.get(0).expect("empty path in restriction").ident;
433+
let crate_root = if ident.is_path_segment_keyword() {
434+
None
435+
} else if ident.span.is_rust_2015() {
436+
Some(Segment::from_ident(Ident::new(
437+
kw::PathRoot,
438+
path.span.shrink_to_lo().with_ctxt(ident.span.ctxt()),
439+
)))
440+
} else {
441+
return Err(RestrictionResolutionError::Relative2018(ident.span, path));
442+
};
443+
444+
let segments = crate_root
445+
.into_iter()
446+
.chain(path.segments.iter().map(|seg| seg.into()))
447+
.collect::<Vec<_>>();
448+
let expected_found_error = |res| {
449+
Err(RestrictionResolutionError::ExpectedFound(
450+
path.span,
451+
Segment::names_to_string(&segments),
452+
res,
453+
))
454+
};
455+
match self.r.resolve_path(
456+
&segments,
457+
Some(TypeNS),
458+
parent_scope,
459+
finalize.then(|| Finalize::new(id, path.span)),
460+
None,
461+
None,
462+
) {
463+
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
464+
let res = module.res().expect("restriction resolved to unnamed block");
465+
if finalize {
466+
self.r.record_partial_res(id, PartialRes::new(res));
467+
}
468+
if module.is_normal() {
469+
if res == Res::Err {
470+
Ok(ty::Restriction::Unrestricted)
471+
} else {
472+
let vis = ty::Visibility::Restricted(res.def_id());
473+
if self.r.is_accessible_from(vis, parent_scope.module) {
474+
Ok(ty::Restriction::Restricted(res.def_id(), restriction.span))
475+
} else {
476+
Err(RestrictionResolutionError::AncestorOnly(path.span))
477+
}
478+
}
479+
} else {
480+
expected_found_error(res)
481+
}
482+
}
483+
PathResult::Module(..) => {
484+
Err(RestrictionResolutionError::ModuleOnly(path.span))
485+
}
486+
PathResult::NonModule(partial_res) => {
487+
expected_found_error(partial_res.base_res())
488+
}
489+
PathResult::Failed { span, label, suggestion, .. } => {
490+
Err(RestrictionResolutionError::FailedToResolve(span, label, suggestion))
491+
}
492+
PathResult::Indeterminate => {
493+
Err(RestrictionResolutionError::Indeterminate(path.span))
494+
}
495+
}
496+
}
497+
}
498+
}
499+
409500
fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) {
410501
let field_vis = fields
411502
.iter()
@@ -809,8 +900,21 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
809900
ItemKind::TyAlias(box TyAlias { ident, .. }) | ItemKind::TraitAlias(ident, ..) => {
810901
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
811902
}
903+
ItemKind::Trait(box ast::Trait { ident, ref impl_restriction, .. }) => {
904+
let impl_restriction = self.resolve_restriction(impl_restriction);
905+
self.r.impl_restrictions.insert(local_def_id, impl_restriction);
812906

813-
ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => {
907+
let module = self.r.new_module(
908+
Some(parent),
909+
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
910+
expansion.to_expn_id(),
911+
item.span,
912+
parent.no_implicit_prelude,
913+
);
914+
self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
915+
self.parent_scope.module = module;
916+
}
917+
ItemKind::Enum(ident, _, _) => {
814918
let module = self.r.new_module(
815919
Some(parent),
816920
ModuleKind::Def(def_kind, def_id, Some(ident.name)),

compiler/rustc_resolve/src/diagnostics.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ use crate::{
4646
AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, BindingKey, Finalize,
4747
ForwardGenericParamBanReason, HasGenericParams, LexicalScopeBinding, MacroRulesScope, Module,
4848
ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult,
49-
PrivacyError, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used,
50-
VisResolutionError, errors as errs, path_names_to_string,
49+
PrivacyError, ResolutionError, Resolver, RestrictionResolutionError, Scope, ScopeSet, Segment,
50+
UseError, Used, VisResolutionError, errors as errs, path_names_to_string,
5151
};
5252

5353
type Res = def::Res<ast::NodeId>;
@@ -1018,6 +1018,46 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
10181018
.emit()
10191019
}
10201020

1021+
pub(crate) fn report_restriction_error(
1022+
&mut self,
1023+
resolution_error: RestrictionResolutionError<'_>,
1024+
) -> ErrorGuaranteed {
1025+
match resolution_error {
1026+
RestrictionResolutionError::Relative2018(span, path) => {
1027+
self.dcx().create_err(errs::RestrictionRelative2018 {
1028+
span,
1029+
path_span: path.span,
1030+
// intentionally converting to String, as the text would also be used as
1031+
// in suggestion context
1032+
path_str: pprust::path_to_string(&path),
1033+
})
1034+
}
1035+
RestrictionResolutionError::AncestorOnly(span) => {
1036+
self.dcx().create_err(errs::RestrictionAncestorOnly(span))
1037+
}
1038+
RestrictionResolutionError::FailedToResolve(span, label, suggestion) => self
1039+
.into_struct_error(
1040+
span,
1041+
ResolutionError::FailedToResolve {
1042+
segment: None,
1043+
label,
1044+
suggestion,
1045+
module: None,
1046+
},
1047+
),
1048+
RestrictionResolutionError::ExpectedFound(span, path_str, res) => {
1049+
self.dcx().create_err(errs::ExpectedModuleFound { span, res, path_str })
1050+
}
1051+
RestrictionResolutionError::Indeterminate(span) => {
1052+
self.dcx().create_err(errs::RestrictionIndeterminate(span))
1053+
}
1054+
RestrictionResolutionError::ModuleOnly(span) => {
1055+
self.dcx().create_err(errs::RestrictionModuleOnly(span))
1056+
}
1057+
}
1058+
.emit()
1059+
}
1060+
10211061
/// Lookup typo candidate in scope for a macro or import.
10221062
fn early_lookup_typo_candidate(
10231063
&mut self,

compiler/rustc_resolve/src/errors.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,3 +1244,25 @@ pub(crate) struct TraitImplMismatch {
12441244
#[label(resolve_trait_impl_mismatch_label_item)]
12451245
pub(crate) trait_item_span: Span,
12461246
}
1247+
1248+
#[derive(Diagnostic)]
1249+
#[diag(resolve_restriction_relative_2018)]
1250+
pub(crate) struct RestrictionRelative2018 {
1251+
#[primary_span]
1252+
pub(crate) span: Span,
1253+
#[suggestion(code = "crate::{path_str}", applicability = "maybe-incorrect")]
1254+
pub(crate) path_span: Span,
1255+
pub(crate) path_str: String,
1256+
}
1257+
1258+
#[derive(Diagnostic)]
1259+
#[diag(resolve_restriction_ancestor_only, code = E0742)]
1260+
pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span);
1261+
1262+
#[derive(Diagnostic)]
1263+
#[diag(resolve_restriction_indeterminate, code = E0578)]
1264+
pub(crate) struct RestrictionIndeterminate(#[primary_span] pub(crate) Span);
1265+
1266+
#[derive(Diagnostic)]
1267+
#[diag(resolve_restriction_module_only)]
1268+
pub(crate) struct RestrictionModuleOnly(#[primary_span] pub(crate) Span);

compiler/rustc_resolve/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,15 @@ enum VisResolutionError<'a> {
321321
ModuleOnly(Span),
322322
}
323323

324+
enum RestrictionResolutionError<'a> {
325+
Relative2018(Span, &'a ast::Path),
326+
AncestorOnly(Span),
327+
FailedToResolve(Span, String, Option<Suggestion>),
328+
ExpectedFound(Span, String, Res),
329+
Indeterminate(Span),
330+
ModuleOnly(Span),
331+
}
332+
324333
/// A minimal representation of a path segment. We use this in resolve because we synthesize 'path
325334
/// segments' which don't have the rest of an AST or HIR `PathSegment`.
326335
#[derive(Clone, Copy, Debug)]
@@ -1101,6 +1110,7 @@ pub struct Resolver<'ra, 'tcx> {
11011110
glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>,
11021111
glob_error: Option<ErrorGuaranteed>,
11031112
visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>,
1113+
impl_restrictions: FxIndexMap<LocalDefId, ty::Restriction>,
11041114
used_imports: FxHashSet<NodeId>,
11051115
maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
11061116

@@ -1492,6 +1502,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14921502
glob_map: Default::default(),
14931503
glob_error: None,
14941504
visibilities_for_hashing: Default::default(),
1505+
impl_restrictions: Default::default(),
14951506
used_imports: FxHashSet::default(),
14961507
maybe_unused_trait_imports: Default::default(),
14971508

@@ -1661,6 +1672,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
16611672
let global_ctxt = ResolverGlobalCtxt {
16621673
expn_that_defined,
16631674
visibilities_for_hashing: self.visibilities_for_hashing,
1675+
impl_restrictions: self.impl_restrictions,
16641676
effective_visibilities,
16651677
extern_crate_map,
16661678
module_children: self.module_children,

0 commit comments

Comments
 (0)