Skip to content

Commit

Permalink
Move a wf-check into the site where the value is instantiated
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Oct 25, 2022
1 parent 1481fd9 commit 8286ea5
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 59 deletions.
71 changes: 53 additions & 18 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT
use rustc_span::edition::Edition;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::Span;
use rustc_span::{sym, Span};
use rustc_target::spec::abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::astconv_object_safety_violations;
Expand Down Expand Up @@ -275,6 +275,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
item_segment.args(),
item_segment.infer_args,
None,
None,
);
if let Some(b) = item_segment.args().bindings.first() {
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
Expand Down Expand Up @@ -324,6 +325,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
generic_args: &'a hir::GenericArgs<'_>,
infer_args: bool,
self_ty: Option<Ty<'tcx>>,
constness: Option<ty::BoundConstness>,
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
// If the type is parameterized by this region, then replace this
// region with the current anon region binding (in other words,
Expand Down Expand Up @@ -534,6 +536,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&mut substs_ctx,
);

if let Some(ty::BoundConstness::ConstIfConst) = constness
&& generics.has_self && !tcx.has_attr(def_id, sym::const_trait)
{
tcx.sess.span_err(
span,
"~const can only be applied to `#[const_trait]` traits",
);
}

(substs, arg_count)
}

Expand Down Expand Up @@ -601,6 +612,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
item_segment.args(),
item_segment.infer_args,
None,
None,
);

if let Some(b) = item_segment.args().bindings.first() {
Expand All @@ -620,6 +632,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&self,
trait_ref: &hir::TraitRef<'_>,
self_ty: Ty<'tcx>,
constness: ty::BoundConstness,
) -> ty::TraitRef<'tcx> {
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});

Expand All @@ -629,6 +642,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self_ty,
trait_ref.path.segments.last().unwrap(),
true,
Some(constness),
)
}

Expand All @@ -655,6 +669,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
args,
infer_args,
Some(self_ty),
Some(constness),
);

let tcx = self.tcx();
Expand All @@ -680,6 +695,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
speculative,
&mut dup_bindings,
binding_span.unwrap_or(binding.span),
constness,
);
// Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
}
Expand Down Expand Up @@ -783,13 +799,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self_ty: Ty<'tcx>,
trait_segment: &hir::PathSegment<'_>,
is_impl: bool,
constness: Option<ty::BoundConstness>,
) -> ty::TraitRef<'tcx> {
let (substs, _) = self.create_substs_for_ast_trait_ref(
span,
trait_def_id,
self_ty,
trait_segment,
is_impl,
constness,
);
if let Some(b) = trait_segment.args().bindings.first() {
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
Expand All @@ -805,6 +823,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self_ty: Ty<'tcx>,
trait_segment: &'a hir::PathSegment<'a>,
is_impl: bool,
constness: Option<ty::BoundConstness>,
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);

Expand All @@ -816,6 +835,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_segment.args(),
trait_segment.infer_args,
Some(self_ty),
constness,
)
}

Expand Down Expand Up @@ -1027,6 +1047,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
speculative: bool,
dup_bindings: &mut FxHashMap<DefId, Span>,
path_span: Span,
constness: ty::BoundConstness,
) -> Result<(), ErrorGuaranteed> {
// Given something like `U: SomeTrait<T = X>`, we want to produce a
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
Expand Down Expand Up @@ -1122,10 +1143,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_ref.substs,
);

debug!(
"add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}",
substs_trait_ref_and_assoc_item
);
debug!(?substs_trait_ref_and_assoc_item);

ty::ProjectionTy {
item_def_id: assoc_item.def_id,
Expand All @@ -1146,8 +1164,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
debug!(?late_bound_in_trait_ref);
debug!(?late_bound_in_ty);

// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
Expand Down Expand Up @@ -1648,6 +1666,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

// Checks that `bounds` contains exactly one element and reports appropriate
// errors otherwise.
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)]
fn one_bound_for_assoc_type<I>(
&self,
all_candidates: impl Fn() -> I,
Expand Down Expand Up @@ -1677,10 +1696,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
return Err(reported);
}
};
debug!("one_bound_for_assoc_type: bound = {:?}", bound);
debug!(?bound);

if let Some(bound2) = next_cand {
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
debug!(?bound2);

let is_equality = is_equality();
let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
Expand Down Expand Up @@ -1776,6 +1795,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// parameter or `Self`.
// NOTE: When this function starts resolving `Trait::AssocTy` successfully
// it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
#[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)]
pub fn associated_path_to_ty(
&self,
hir_ref_id: hir::HirId,
Expand All @@ -1793,8 +1813,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Res::Err
};

debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident);

// Check if we have an enum variant.
let mut variant_resolution = None;
if let ty::Adt(adt_def, _) = qself_ty.kind() {
Expand Down Expand Up @@ -2050,6 +2068,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
item_def_id: DefId,
trait_segment: &hir::PathSegment<'_>,
item_segment: &hir::PathSegment<'_>,
constness: ty::BoundConstness,
) -> Ty<'tcx> {
let tcx = self.tcx();

Expand Down Expand Up @@ -2094,8 +2113,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

debug!("qpath_to_ty: self_type={:?}", self_ty);

let trait_ref =
self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false);
let trait_ref = self.ast_path_to_mono_trait_ref(
span,
trait_def_id,
self_ty,
trait_segment,
false,
Some(constness),
);

let item_substs = self.create_substs_for_associated_item(
span,
Expand Down Expand Up @@ -2534,12 +2559,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Res::Def(DefKind::AssocTy, def_id) => {
debug_assert!(path.segments.len() >= 2);
self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {});
// HACK: until we support `<Type as ~const Trait>`, assume all of them are.
let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) {
ty::BoundConstness::ConstIfConst
} else {
ty::BoundConstness::NotConst
};
self.qpath_to_ty(
span,
opt_self_ty,
def_id,
&path.segments[path.segments.len() - 2],
path.segments.last().unwrap(),
constness,
)
}
Res::PrimTy(prim_ty) => {
Expand Down Expand Up @@ -2658,6 +2690,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&GenericArgs::none(),
true,
None,
None,
);
EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
.subst(tcx, substs)
Expand Down Expand Up @@ -2766,6 +2799,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}

#[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)]
pub fn ty_of_fn(
&self,
hir_id: hir::HirId,
Expand All @@ -2775,8 +2809,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
generics: Option<&hir::Generics<'_>>,
hir_ty: Option<&hir::Ty<'_>>,
) -> ty::PolyFnSig<'tcx> {
debug!("ty_of_fn");

let tcx = self.tcx();
let bound_vars = tcx.late_bound_vars(hir_id);
debug!(?bound_vars);
Expand Down Expand Up @@ -2826,7 +2858,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
};

debug!("ty_of_fn: output_ty={:?}", output_ty);
debug!(?output_ty);

let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
Expand Down Expand Up @@ -2903,8 +2935,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };

let trait_ref =
self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
let trait_ref = self.instantiate_mono_trait_ref(
i.of_trait.as_ref()?,
self.ast_ty_to_ty(i.self_ty),
ty::BoundConstness::NotConst,
);

let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
tcx,
Expand Down
33 changes: 30 additions & 3 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1143,7 +1143,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
}

ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
// Do not try to inference the return type for a impl method coming from a trait
// Do not try to infer the return type for a impl method coming from a trait
if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
tcx.hir().get(tcx.hir().get_parent_node(hir_id))
&& i.of_trait.is_some()
Expand Down Expand Up @@ -1286,10 +1286,37 @@ fn infer_return_ty_for_fn_sig<'tcx>(

fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
let icx = ItemCtxt::new(tcx, def_id);
match tcx.hir().expect_item(def_id.expect_local()).kind {
let item = tcx.hir().expect_item(def_id.expect_local());
match item.kind {
hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
let selfty = tcx.type_of(def_id);
<dyn AstConv<'_>>::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty)
<dyn AstConv<'_>>::instantiate_mono_trait_ref(
&icx,
ast_trait_ref,
selfty,
match impl_.constness {
hir::Constness::Const => {
if let Some(trait_def_id) = ast_trait_ref.trait_def_id() && !tcx.has_attr(trait_def_id, sym::const_trait) {
let trait_name = tcx.item_name(trait_def_id);
let mut err = tcx.sess.struct_span_err(
ast_trait_ref.path.span,
&format!("const `impl` for trait `{trait_name}` which is not marked with `#[const_trait]`"),
);
if trait_def_id.is_local() {
let sp = tcx.def_span(trait_def_id).shrink_to_lo();
err.span_suggestion(sp, &format!("mark `{trait_name}` as const"), "#[const_trait]", rustc_errors::Applicability::MachineApplicable);
}
err.note("marking a trait with `#[const_trait]` ensures all default method bodies are `const`");
err.note("adding a non-const method body in the future would be a breaking change");
err.emit();
ty::BoundConstness::NotConst
} else {
ty::BoundConstness::ConstIfConst
}
},
hir::Constness::NotConst => ty::BoundConstness::NotConst,
},
)
}),
_ => bug!(),
}
Expand Down
26 changes: 0 additions & 26 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,32 +303,6 @@ impl<'tcx> WfPredicates<'tcx> {
let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
} else {
if !tcx.has_attr(trait_ref.def_id, rustc_span::sym::const_trait) {
if let Some(item) = self.item &&
let hir::ItemKind::Impl(impl_) = item.kind &&
let Some(trait_) = &impl_.of_trait &&
let Some(def_id) = trait_.trait_def_id() &&
def_id == trait_ref.def_id
{
let trait_name = tcx.item_name(def_id);
let mut err = tcx.sess.struct_span_err(
self.span,
&format!("const `impl` for trait `{trait_name}` which is not marked with `#[const_trait]`"),
);
if def_id.is_local() {
let sp = tcx.def_span(def_id).shrink_to_lo();
err.span_suggestion(sp, &format!("mark `{trait_name}` as const"), "#[const_trait]", rustc_errors::Applicability::MachineApplicable);
}
err.note("marking a trait with `#[const_trait]` ensures all default method bodies are `const`");
err.note("adding a non-const method body in the future would be a breaking change");
err.emit();
} else {
tcx.sess.span_err(
self.span,
"~const can only be applied to `#[const_trait]` traits",
);
}
}
self.nominal_obligations(trait_ref.def_id, trait_ref.substs)
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: ~const can only be applied to `#[const_trait]` traits
--> $DIR/super-traits-fail-2.rs:11:12
--> $DIR/super-traits-fail-2.rs:11:19
|
LL | trait Bar: ~const Foo {}
| ^^^^^^^^^^
| ^^^

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: ~const can only be applied to `#[const_trait]` traits
--> $DIR/super-traits-fail-2.rs:11:12
--> $DIR/super-traits-fail-2.rs:11:19
|
LL | trait Bar: ~const Foo {}
| ^^^^^^^^^^
| ^^^

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
error: ~const can only be applied to `#[const_trait]` traits
--> $DIR/super-traits-fail-3.rs:12:12
--> $DIR/super-traits-fail-3.rs:12:19
|
LL | trait Bar: ~const Foo {}
| ^^^^^^^^^^
| ^^^

error: ~const can only be applied to `#[const_trait]` traits
--> $DIR/super-traits-fail-3.rs:15:17
--> $DIR/super-traits-fail-3.rs:15:24
|
LL | const fn foo<T: ~const Bar>(x: &T) {
| ^^^^^^^^^^
| ^^^

error: aborting due to 2 previous errors

Loading

0 comments on commit 8286ea5

Please sign in to comment.