Auto merge of #105961 - fmease:iat-type-directed-probing, r=jackh726

Type-directed probing for inherent associated types

When probing for inherent associated types (IATs), equate the Self-type found in the projection with the Self-type of the relevant inherent impl blocks and check if all predicates are satisfied.
Previously, we didn't look at the Self-type or at the bounds and just picked the first inherent impl block containing an associated type with the name we were searching for which is obviously incorrect.

Regarding the implementation, I basically copied what we do during method probing (`assemble_inherent_impl_probe`, `consider_probe`). Unfortunately, I had to duplicate a lot of the diagnostic code found in `rustc_hir_typeck::method::suggest` which we don't have access to in `rustc_hir_analysis`. Not sure if there is a simple way to unify the error handling. Note that in the future, `rustc_hir_analysis::astconv` might not actually be the place where we resolve inherent associated types (see https://github.com/rust-lang/rust/pull/103621#issuecomment-1304309565) but `rustc_hir_typeck` (?) in which case the duplication may naturally just disappear. While inherent associated *constants* are currently resolved during "method" probing, I did not find a straightforward way to incorporate IAT lookup into it as types and values (functions & constants) are two separate entities for which distinct code paths are taken.

Fixes #104251 (incl. https://github.com/rust-lang/rust/issues/104251#issuecomment-1338501171).
Fixes #105305.
Fixes #107468.

`@rustbot` label T-types F-inherent_associated_types
r? types
This commit is contained in:
bors 2023-02-20 00:37:20 +00:00
commit 7b552967b8
29 changed files with 946 additions and 84 deletions

View File

@ -1,10 +1,11 @@
use crate::astconv::AstConv;
use crate::errors::{ManualImplementation, MissingTypeParams};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty;
use rustc_infer::traits::FulfillmentError;
use rustc_middle::ty::{self, Ty};
use rustc_session::parse::feature_err;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident};
@ -221,6 +222,231 @@ pub(crate) fn complain_about_assoc_type_not_found<I>(
err.emit()
}
pub(crate) fn complain_about_ambiguous_inherent_assoc_type(
&self,
name: Ident,
candidates: Vec<DefId>,
span: Span,
) -> ErrorGuaranteed {
let mut err = struct_span_err!(
self.tcx().sess,
name.span,
E0034,
"multiple applicable items in scope"
);
err.span_label(name.span, format!("multiple `{name}` found"));
self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span);
err.emit()
}
// FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
fn note_ambiguous_inherent_assoc_type(
&self,
err: &mut Diagnostic,
candidates: Vec<DefId>,
span: Span,
) {
let tcx = self.tcx();
// Dynamic limit to avoid hiding just one candidate, which is silly.
let limit = if candidates.len() == 5 { 5 } else { 4 };
for (index, &item) in candidates.iter().take(limit).enumerate() {
let impl_ = tcx.impl_of_method(item).unwrap();
let note_span = if item.is_local() {
Some(tcx.def_span(item))
} else if impl_.is_local() {
Some(tcx.def_span(impl_))
} else {
None
};
let title = if candidates.len() > 1 {
format!("candidate #{}", index + 1)
} else {
"the candidate".into()
};
let impl_ty = tcx.at(span).type_of(impl_).subst_identity();
let note = format!("{title} is defined in an impl for the type `{impl_ty}`");
if let Some(span) = note_span {
err.span_note(span, &note);
} else {
err.note(&note);
}
}
if candidates.len() > limit {
err.note(&format!("and {} others", candidates.len() - limit));
}
}
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
pub(crate) fn complain_about_inherent_assoc_type_not_found(
&self,
name: Ident,
self_ty: Ty<'tcx>,
candidates: Vec<(DefId, (DefId, DefId))>,
fulfillment_errors: Vec<FulfillmentError<'tcx>>,
span: Span,
) -> ErrorGuaranteed {
// FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`.
// Either
// * update this code by applying changes similar to #106702 or by taking a
// Vec<(DefId, (DefId, DefId), Option<Vec<FulfillmentError<'tcx>>>)> or
// * deduplicate this code across the two crates.
let tcx = self.tcx();
let adt_did = self_ty.ty_adt_def().map(|def| def.did());
let add_def_label = |err: &mut Diagnostic| {
if let Some(did) = adt_did {
err.span_label(
tcx.def_span(did),
format!(
"associated item `{name}` not found for this {}",
tcx.def_kind(did).descr(did)
),
);
}
};
if fulfillment_errors.is_empty() {
// FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate.
let limit = if candidates.len() == 5 { 5 } else { 4 };
let type_candidates = candidates
.iter()
.take(limit)
.map(|&(impl_, _)| format!("- `{}`", tcx.at(span).type_of(impl_).subst_identity()))
.collect::<Vec<_>>()
.join("\n");
let additional_types = if candidates.len() > limit {
format!("\nand {} more types", candidates.len() - limit)
} else {
String::new()
};
let mut err = struct_span_err!(
tcx.sess,
name.span,
E0220,
"associated type `{name}` not found for `{self_ty}` in the current scope"
);
err.span_label(name.span, format!("associated item not found in `{self_ty}`"));
err.note(&format!(
"the associated type was found for\n{type_candidates}{additional_types}",
));
add_def_label(&mut err);
return err.emit();
}
let mut bound_spans = Vec::new();
let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
let msg = format!(
"doesn't satisfy `{}`",
if obligation.len() > 50 { quiet } else { obligation }
);
match &self_ty.kind() {
// Point at the type that couldn't satisfy the bound.
ty::Adt(def, _) => bound_spans.push((tcx.def_span(def.did()), msg)),
// Point at the trait object that couldn't satisfy the bound.
ty::Dynamic(preds, _, _) => {
for pred in preds.iter() {
match pred.skip_binder() {
ty::ExistentialPredicate::Trait(tr) => {
bound_spans.push((tcx.def_span(tr.def_id), msg.clone()))
}
ty::ExistentialPredicate::Projection(_)
| ty::ExistentialPredicate::AutoTrait(_) => {}
}
}
}
// Point at the closure that couldn't satisfy the bound.
ty::Closure(def_id, _) => {
bound_spans.push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`")))
}
_ => {}
}
};
let format_pred = |pred: ty::Predicate<'tcx>| {
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
let pred = bound_predicate.rebind(pred);
// `<Foo as Iterator>::Item = String`.
let projection_ty = pred.skip_binder().projection_ty;
let substs_with_infer_self = tcx.mk_substs(
std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
.chain(projection_ty.substs.iter().skip(1)),
);
let quiet_projection_ty =
tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self);
let term = pred.skip_binder().term;
let obligation = format!("{projection_ty} = {term}");
let quiet = format!("{quiet_projection_ty} = {term}");
bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
Some((obligation, projection_ty.self_ty()))
}
ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
let p = poly_trait_ref.trait_ref;
let self_ty = p.self_ty();
let path = p.print_only_trait_path();
let obligation = format!("{self_ty}: {path}");
let quiet = format!("_: {path}");
bound_span_label(self_ty, &obligation, &quiet);
Some((obligation, self_ty))
}
_ => None,
}
};
// FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds.
// I would do the same here if it didn't mean more code duplication.
let mut bounds: Vec<_> = fulfillment_errors
.into_iter()
.map(|error| error.root_obligation.predicate)
.filter_map(format_pred)
.map(|(p, _)| format!("`{}`", p))
.collect();
bounds.sort();
bounds.dedup();
let mut err = tcx.sess.struct_span_err(
name.span,
&format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
);
if !bounds.is_empty() {
err.note(&format!(
"the following trait bounds were not satisfied:\n{}",
bounds.join("\n")
));
}
err.span_label(
name.span,
format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds")
);
bound_spans.sort();
bound_spans.dedup();
for (span, msg) in bound_spans {
if !tcx.sess.source_map().is_span_accessible(span) {
continue;
}
err.span_label(span, &msg);
}
add_def_label(&mut err);
err.emit()
}
/// When there are any missing associated types, emit an E0191 error and attempt to supply a
/// reasonable suggestion on how to write it. For the case of multiple associated types in the
/// same trait bound have the same name (as they come from different supertraits), we instead

View File

@ -27,7 +27,10 @@
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{walk_generics, Visitor as _};
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::DynKind;
@ -39,12 +42,11 @@
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, Span, DUMMY_SP};
use rustc_target::spec::abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::astconv_object_safety_violations;
use rustc_trait_selection::traits::error_reporting::{
report_object_safety_error, suggestions::NextTypeParamName,
};
use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, astconv_object_safety_violations, ObligationCtxt};
use smallvec::{smallvec, SmallVec};
use std::collections::BTreeSet;
@ -1944,7 +1946,7 @@ pub fn associated_path_to_ty(
Res::Err
};
// Check if we have an enum variant.
// Check if we have an enum variant or an inherent associated type.
let mut variant_resolution = None;
if let Some(adt_def) = self.probe_adt(span, qself_ty) {
if adt_def.is_enum() {
@ -2043,23 +2045,15 @@ pub fn associated_path_to_ty(
}
}
// see if we can satisfy using an inherent associated type
for &impl_ in tcx.inherent_impls(adt_def.did()) {
let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else {
continue;
};
let ty::Adt(_, adt_substs) = qself_ty.kind() else {
// FIXME(inherent_associated_types)
bug!("unimplemented: non-adt self of inherent assoc ty");
};
let item_substs = self.create_substs_for_associated_item(
span,
assoc_ty_did,
assoc_segment,
adt_substs,
);
let ty = tcx.type_of(assoc_ty_did).subst(tcx, item_substs);
return Ok((ty, DefKind::AssocTy, assoc_ty_did));
if let Some((ty, did)) = self.lookup_inherent_assoc_ty(
assoc_ident,
assoc_segment,
adt_def.did(),
qself_ty,
hir_ref_id,
span,
)? {
return Ok((ty, DefKind::AssocTy, did));
}
}
@ -2202,6 +2196,172 @@ pub fn associated_path_to_ty(
Ok((ty, DefKind::AssocTy, assoc_ty_did))
}
fn lookup_inherent_assoc_ty(
&self,
name: Ident,
segment: &hir::PathSegment<'_>,
adt_did: DefId,
self_ty: Ty<'tcx>,
block: hir::HirId,
span: Span,
) -> Result<Option<(Ty<'tcx>, DefId)>, ErrorGuaranteed> {
let tcx = self.tcx();
let candidates: Vec<_> = tcx
.inherent_impls(adt_did)
.iter()
.filter_map(|&impl_| Some((impl_, self.lookup_assoc_ty_unchecked(name, block, impl_)?)))
.collect();
if candidates.is_empty() {
return Ok(None);
}
// In contexts that have no inference context, just make a new one.
// We do need a local variable to store it, though.
let infcx_;
let infcx = match self.infcx() {
Some(infcx) => infcx,
None => {
assert!(!self_ty.needs_infer());
infcx_ = tcx.infer_ctxt().ignoring_regions().build();
&infcx_
}
};
let param_env = tcx.param_env(block.owner.to_def_id());
let cause = ObligationCause::misc(span, block.owner.def_id);
let mut fulfillment_errors = Vec::new();
let mut applicable_candidates: Vec<_> = candidates
.iter()
.filter_map(|&(impl_, (assoc_item, def_scope))| {
infcx.probe(|_| {
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
let impl_ty = tcx.type_of(impl_);
let impl_substs = infcx.fresh_item_substs(impl_);
let impl_ty = impl_ty.subst(tcx, impl_substs);
let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
// Check that the Self-types can be related.
// FIXME(fmease): Should we use `eq` here?
ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?;
// Check whether the impl imposes obligations we have to worry about.
let impl_bounds = tcx.predicates_of(impl_);
let impl_bounds = impl_bounds.instantiate(tcx, impl_substs);
let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds);
let impl_obligations = traits::predicates_for_generics(
|_, _| cause.clone(),
param_env,
impl_bounds,
);
ocx.register_obligations(impl_obligations);
let mut errors = ocx.select_where_possible();
if !errors.is_empty() {
fulfillment_errors.append(&mut errors);
return None;
}
// FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot.
Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs)))
})
})
.collect();
if applicable_candidates.len() > 1 {
return Err(self.complain_about_ambiguous_inherent_assoc_type(
name,
applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(),
span,
));
}
if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() {
self.check_assoc_ty(assoc_item, name, def_scope, block, span);
// FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, we still
// need to relate the Self-type with fresh item substs & register region obligations for
// regionck to prove/disprove.
let item_substs =
self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs);
// FIXME(fmease, #106722): Check if the bounds on the parameters of the
// associated type hold, if any.
let ty = tcx.type_of(assoc_item).subst(tcx, item_substs);
return Ok(Some((ty, assoc_item)));
}
Err(self.complain_about_inherent_assoc_type_not_found(
name,
self_ty,
candidates,
fulfillment_errors,
span,
))
}
fn lookup_assoc_ty(
&self,
name: Ident,
block: hir::HirId,
span: Span,
scope: DefId,
) -> Option<DefId> {
let (item, def_scope) = self.lookup_assoc_ty_unchecked(name, block, scope)?;
self.check_assoc_ty(item, name, def_scope, block, span);
Some(item)
}
fn lookup_assoc_ty_unchecked(
&self,
name: Ident,
block: hir::HirId,
scope: DefId,
) -> Option<(DefId, DefId)> {
let tcx = self.tcx();
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block);
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `find_by_name_and_kind`.
let item = tcx.associated_items(scope).in_definition_order().find(|i| {
i.kind.namespace() == Namespace::TypeNS
&& i.ident(tcx).normalize_to_macros_2_0() == ident
})?;
Some((item.def_id, def_scope))
}
fn check_assoc_ty(
&self,
item: DefId,
name: Ident,
def_scope: DefId,
block: hir::HirId,
span: Span,
) {
let tcx = self.tcx();
let kind = DefKind::AssocTy;
if !tcx.visibility(item).is_accessible_from(def_scope, tcx) {
let kind = kind.descr(item);
let msg = format!("{kind} `{name}` is private");
let def_span = tcx.def_span(item);
tcx.sess
.struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624))
.span_label(span, &format!("private {kind}"))
.span_label(def_span, &format!("{kind} defined here"))
.emit();
}
tcx.check_stability(item, Some(block), span, None);
}
fn probe_traits_that_match_assoc_ty(
&self,
qself_ty: Ty<'tcx>,
@ -2255,39 +2415,6 @@ fn probe_traits_that_match_assoc_ty(
.collect()
}
fn lookup_assoc_ty(
&self,
ident: Ident,
block: hir::HirId,
span: Span,
scope: DefId,
) -> Option<DefId> {
let tcx = self.tcx();
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block);
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `find_by_name_and_kind`.
let item = tcx.associated_items(scope).in_definition_order().find(|i| {
i.kind.namespace() == Namespace::TypeNS
&& i.ident(tcx).normalize_to_macros_2_0() == ident
})?;
let kind = DefKind::AssocTy;
if !item.visibility(tcx).is_accessible_from(def_scope, tcx) {
let kind = kind.descr(item.def_id);
let msg = format!("{kind} `{ident}` is private");
let def_span = self.tcx().def_span(item.def_id);
tcx.sess
.struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624))
.span_label(span, &format!("private {kind}"))
.span_label(def_span, &format!("{kind} defined here"))
.emit();
}
tcx.check_stability(item.def_id, Some(block), span, None);
Some(item.def_id)
}
fn qpath_to_ty(
&self,
span: Span,
@ -3375,3 +3502,36 @@ fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
}
}
}
pub trait InferCtxtExt<'tcx> {
fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx>;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. } => self
.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::SubstitutionPlaceholder,
span: self.tcx.def_span(def_id),
})
.into(),
GenericParamDefKind::Const { .. } => {
let span = self.tcx.def_span(def_id);
let origin = ConstVariableOrigin {
kind: ConstVariableOriginKind::SubstitutionPlaceholder,
span,
};
self.next_const_var(
self.tcx
.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic"),
origin,
)
.into()
}
})
}
}

View File

@ -9,12 +9,11 @@
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir_analysis::astconv::InferCtxtExt as _;
use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::OriginalQueryValues;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::middle::stability;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::AssocItem;
@ -1941,33 +1940,6 @@ fn impl_ty_and_substs(
(self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
}
fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. } => self
.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::SubstitutionPlaceholder,
span: self.tcx.def_span(def_id),
})
.into(),
GenericParamDefKind::Const { .. } => {
let span = self.tcx.def_span(def_id);
let origin = ConstVariableOrigin {
kind: ConstVariableOriginKind::SubstitutionPlaceholder,
span,
};
self.next_const_var(
self.tcx
.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic"),
origin,
)
.into()
}
})
}
/// Replaces late-bound-regions bound by `value` with `'static` using
/// `ty::erase_late_bound_regions`.
///

View File

@ -0,0 +1,16 @@
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct Wrapper<T>(T);
impl Wrapper<i32> {
type Foo = i32;
}
impl Wrapper<()> {
type Foo = ();
}
fn main() {
let _: Wrapper<_>::Foo = (); //~ ERROR multiple applicable items in scope
}

View File

@ -0,0 +1,20 @@
error[E0034]: multiple applicable items in scope
--> $DIR/ambiguity.rs:15:24
|
LL | let _: Wrapper<_>::Foo = ();
| ^^^ multiple `Foo` found
|
note: candidate #1 is defined in an impl for the type `Wrapper<i32>`
--> $DIR/ambiguity.rs:7:5
|
LL | type Foo = i32;
| ^^^^^^^^
note: candidate #2 is defined in an impl for the type `Wrapper<()>`
--> $DIR/ambiguity.rs:11:5
|
LL | type Foo = ();
| ^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0034`.

View File

@ -0,0 +1,23 @@
// known-bug: unknown
// failure-status: 101
// normalize-stderr-test "note: .*\n\n" -> ""
// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
// rustc-env:RUST_BACKTRACE=0
// FIXME: I presume a type variable that couldn't be solved by `resolve_vars_if_possible`
// escapes the InferCtxt snapshot.
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct Cont<T>(T);
impl<T: Copy> Cont<T> {
type Out = Vec<T>;
}
pub fn weird<T: Copy>(x: T) {
let _: Cont<_>::Out = vec![true];
}
fn main() {}

View File

@ -0,0 +1,6 @@
error: the compiler unexpectedly panicked. this is a bug.
query stack during panic:
#0 [typeck] type-checking `weird`
#1 [typeck_item_bodies] type-checking all item bodies
end of query stack

View File

@ -0,0 +1,15 @@
// known-bug: unknown
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct S<T>(T);
impl S<()> {
type P = i128;
}
fn main() {
// We fail to infer `_ == ()` here.
let _: S<_>::P;
}

View File

@ -0,0 +1,9 @@
error[E0282]: type annotations needed
--> $DIR/inference-fail.rs:14:14
|
LL | let _: S<_>::P;
| ^ cannot infer type
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.

View File

@ -0,0 +1,19 @@
// known-bug: unknown
// check-pass
// We currently don't region-check inherent associated type projections at all.
#![feature(inherent_associated_types)]
#![allow(incomplete_features, dead_code)]
struct S<T>(T);
impl S<&'static ()> {
type T = ();
}
fn usr<'a>() {
let _: S::<&'a ()>::T; // this should *fail* but it doesn't!
}
fn main() {}

View File

@ -0,0 +1,41 @@
// check-pass
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
// Check that inherent associated types are dispatched on the concrete Self type.
struct Select<T>(T);
impl Select<u8> {
type Projection = ();
}
impl Select<String> {
type Projection = bool;
}
struct Choose<T>(T);
struct NonCopy;
impl<T: Copy> Choose<T> {
type Result = Vec<T>;
}
impl Choose<NonCopy> {
type Result = ();
}
fn main() {
let _: Select<String>::Projection = false;
let _: Select<u8>::Projection = ();
let _: Choose<NonCopy>::Result = ();
let _: Choose<bool>::Result = vec![true];
}
// Test if we use the correct `ParamEnv` when proving obligations.
pub fn parameterized<T: Copy>(x: T) {
let _: Choose<T>::Result = vec![x];
}

View File

@ -0,0 +1,39 @@
// check-pass
#![feature(inherent_associated_types, auto_traits, negative_impls)]
#![allow(incomplete_features)]
use std::cmp::Ordering;
// Check that inherent associated types are dispatched on the concrete Self type.
struct Select<T, U>(T, U);
impl<T: Ordinary, U: Ordinary> Select<T, U> {
type Type = ();
}
impl<T: Ordinary> Select<T, Special> {
type Type = bool;
}
impl<T: Ordinary> Select<Special, T> {
type Type = Ordering;
}
impl Select<Special, Special> {
type Type = (bool, bool);
}
fn main() {
let _: Select<String, Special>::Type = false;
let _: Select<Special, Special>::Type = (true, false);
let _: Select<Special, u8>::Type = Ordering::Equal;
let _: Select<i128, ()>::Type = ();
}
enum Special {}
impl !Ordinary for Special {}
auto trait Ordinary {}

View File

@ -0,0 +1,17 @@
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct Parameterized<T, U>(T, U);
impl Parameterized<(), ()> {
type Output = bool;
}
impl<T> Parameterized<bool, T> {
type Result = T;
}
fn main() {
let _: Parameterized<(), ()>::Output = String::new(); //~ ERROR mismatched types
let _: Parameterized<bool, u32>::Result = (); //~ ERROR mismatched types
}

View File

@ -0,0 +1,19 @@
error[E0308]: mismatched types
--> $DIR/dispatch-on-self-type-2.rs:15:44
|
LL | let _: Parameterized<(), ()>::Output = String::new();
| ----------------------------- ^^^^^^^^^^^^^ expected `bool`, found `String`
| |
| expected due to this
error[E0308]: mismatched types
--> $DIR/dispatch-on-self-type-2.rs:16:47
|
LL | let _: Parameterized<bool, u32>::Result = ();
| -------------------------------- ^^ expected `u32`, found `()`
| |
| expected due to this
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -0,0 +1,31 @@
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
// Check that it's okay to report “[inherent] associated type […] not found” for inherent associated
// type candidates that are not applicable (due to unsuitable Self type) even if there exists a
// “shadowed” associated type from a trait with the same name since its use would be ambiguous
// anyway if the IAT didn't exist.
// FIXME(inherent_associated_types): Figure out which error would be more helpful here.
// revisions: shadowed uncovered
struct S<T>(T);
trait Tr {
type Pr;
}
impl<T> Tr for S<T> {
type Pr = ();
}
#[cfg(shadowed)]
impl S<()> {
type Pr = i32;
}
fn main() {
let _: S::<bool>::Pr = ();
//[shadowed]~^ ERROR associated type `Pr` not found
//[uncovered]~^^ ERROR ambiguous associated type
}

View File

@ -0,0 +1,15 @@
error[E0220]: associated type `Pr` not found for `S<bool>` in the current scope
--> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23
|
LL | struct S<T>(T);
| ----------- associated item `Pr` not found for this struct
...
LL | let _: S::<bool>::Pr = ();
| ^^ associated item not found in `S<bool>`
|
= note: the associated type was found for
- `S<()>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0220`.

View File

@ -0,0 +1,9 @@
error[E0223]: ambiguous associated type
--> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:12
|
LL | let _: S::<bool>::Pr = ();
| ^^^^^^^^^^^^^ help: use the fully-qualified path: `<S<bool> as Tr>::Pr`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0223`.

View File

@ -0,0 +1,16 @@
error[E0220]: associated type `Proj` not found for `Family<Option<()>>` in the current scope
--> $DIR/not-found-self-type-differs.rs:17:34
|
LL | struct Family<T>(T);
| ---------------- associated item `Proj` not found for this struct
...
LL | type Alias = Family<Option<()>>::Proj;
| ^^^^ associated item not found in `Family<Option<()>>`
|
= note: the associated type was found for
- `Family<()>`
- `Family<Result<T, ()>>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0220`.

View File

@ -0,0 +1,16 @@
error[E0220]: associated type `Proj` not found for `Family<PathBuf>` in the current scope
--> $DIR/not-found-self-type-differs.rs:21:40
|
LL | struct Family<T>(T);
| ---------------- associated item `Proj` not found for this struct
...
LL | let _: Family<std::path::PathBuf>::Proj = ();
| ^^^^ associated item not found in `Family<PathBuf>`
|
= note: the associated type was found for
- `Family<()>`
- `Family<Result<T, ()>>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0220`.

View File

@ -0,0 +1,22 @@
// revisions: local alias
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct Family<T>(T);
impl Family<()> {
type Proj = ();
}
impl<T> Family<Result<T, ()>> {
type Proj = Self;
}
#[cfg(alias)]
type Alias = Family<Option<()>>::Proj; //[alias]~ ERROR associated type `Proj` not found for `Family<Option<()>>`
fn main() {
#[cfg(local)]
let _: Family<std::path::PathBuf>::Proj = (); //[local]~ ERROR associated type `Proj` not found for `Family<PathBuf>`
}

View File

@ -0,0 +1,21 @@
// Regression test for issue #104251.
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct Container<T: ?Sized>(T);
impl<T> Container<T> {
type Yield = i32;
}
struct Duple<T, U>(T, U);
impl<T: Copy, U: Send> Duple<T, U> {
type Combination = (T, U);
}
fn main() {
let _: Container<[u8]>::Yield = 1; //~ ERROR the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied
let _: Duple<String, std::rc::Rc<str>>::Combination; //~ ERROR the associated type `Combination` exists for `Duple<String, Rc<str>>`, but its trait bounds were not satisfied
}

View File

@ -0,0 +1,27 @@
error: the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied
--> $DIR/not-found-unsatisfied-bounds-0.rs:19:29
|
LL | struct Container<T: ?Sized>(T);
| --------------------------- associated item `Yield` not found for this struct
...
LL | let _: Container<[u8]>::Yield = 1;
| ^^^^^ associated type cannot be referenced on `Container<[u8]>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`[u8]: Sized`
error: the associated type `Combination` exists for `Duple<String, Rc<str>>`, but its trait bounds were not satisfied
--> $DIR/not-found-unsatisfied-bounds-0.rs:20:45
|
LL | struct Duple<T, U>(T, U);
| ------------------ associated item `Combination` not found for this struct
...
LL | let _: Duple<String, std::rc::Rc<str>>::Combination;
| ^^^^^^^^^^^ associated type cannot be referenced on `Duple<String, Rc<str>>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Rc<str>: Send`
`String: Copy`
error: aborting due to 2 previous errors

View File

@ -0,0 +1,18 @@
// fail-check
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
// Test if we use the correct `ParamEnv` when proving obligations.
fn parameterized<T>() {
let _: Container<T>::Proj = String::new(); //~ ERROR the associated type `Proj` exists for `Container<T>`, but its trait bounds were not satisfied
}
struct Container<T>(T);
impl<T: Clone> Container<T> {
type Proj = String;
}
fn main() {}

View File

@ -0,0 +1,14 @@
error: the associated type `Proj` exists for `Container<T>`, but its trait bounds were not satisfied
--> $DIR/not-found-unsatisfied-bounds-1.rs:9:26
|
LL | let _: Container<T>::Proj = String::new();
| ^^^^ associated type cannot be referenced on `Container<T>` due to unsatisfied trait bounds
...
LL | struct Container<T>(T);
| ------------------- associated item `Proj` not found for this struct
|
= note: the following trait bounds were not satisfied:
`T: Clone`
error: aborting due to previous error

View File

@ -0,0 +1,20 @@
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct S<A, B>(A, B);
struct Featureless;
trait One {}
trait Two {}
impl<T: One> S<Featureless, T> {
type X = ();
}
impl<T: Two> S<T, Featureless> {
type X = String;
}
fn main() {
let _: S::<Featureless, Featureless>::X; //~ ERROR the associated type `X` exists for `S<Featureless, Featureless>`, but its trait bounds were not satisfied
}

View File

@ -0,0 +1,20 @@
error: the associated type `X` exists for `S<Featureless, Featureless>`, but its trait bounds were not satisfied
--> $DIR/not-found-unsatisfied-bounds-in-multiple-impls.rs:19:43
|
LL | struct S<A, B>(A, B);
| -------------- associated item `X` not found for this struct
LL | struct Featureless;
| ------------------
| |
| doesn't satisfy `Featureless: One`
| doesn't satisfy `Featureless: Two`
...
LL | let _: S::<Featureless, Featureless>::X;
| ^ associated type cannot be referenced on `S<Featureless, Featureless>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Featureless: One`
`Featureless: Two`
error: aborting due to previous error

View File

@ -0,0 +1,23 @@
// Regression test for issue #105305 and for
// https://github.com/rust-lang/rust/issues/107468#issuecomment-1409096700
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
struct S<T>(T);
impl<T, 'a> S<T> { //~ ERROR lifetime parameters must be declared prior to type and const parameters
type P = T;
}
struct Subj<T>(T);
impl<T, S> Subj<(T, S)> {
type Un = (T, S);
}
fn main() {
type A = S<()>::P;
let _: Subj<(i32, i32)>::Un = 0i32; //~ ERROR mismatched types
}

View File

@ -0,0 +1,20 @@
error: lifetime parameters must be declared prior to type and const parameters
--> $DIR/substitute-params-bad.rs:9:9
|
LL | impl<T, 'a> S<T> {
| ----^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, T>`
error[E0308]: mismatched types
--> $DIR/substitute-params-bad.rs:22:35
|
LL | let _: Subj<(i32, i32)>::Un = 0i32;
| -------------------- ^^^^ expected `(i32, i32)`, found `i32`
| |
| expected due to this
|
= note: expected tuple `(i32, i32)`
found type `i32`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -9,7 +9,15 @@ impl<T> S<T> {
type P = T;
}
impl<T> S<(T,)> {
type Un = T;
}
fn main() {
// Regression test for issue #104240.
type A = S<()>::P;
let _: A = ();
// Regression test for issue #107468.
let _: S<(i32,)>::Un = 0i32;
}