Auto merge of #123484 - jhpratt:rollup-usz4e64, r=jhpratt
Rollup of 9 pull requests Successful merges: - #123206 (Require Pointee::Metadata to be Freeze) - #123363 (change `NormalizesTo` to fully structurally normalize) - #123407 (Default to light theme if JS is enabled but not working) - #123417 (Add Description for cargo in rustdoc documentation) - #123437 (Manually run `clang-format` on `CoverageMappingWrapper.cpp`) - #123454 (hir: Use `ItemLocalId::ZERO` in a couple more places) - #123464 (Cleanup: Rename `HAS_PROJECTIONS` to `HAS_ALIASES` etc.) - #123477 (do not ICE in `fn forced_ambiguity` if we get an error) - #123478 (CFI: Add test for `call_once` addr taken) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
3d7e88148a
@ -19,7 +19,7 @@ struct NodeCollector<'a, 'hir> {
|
||||
parenting: LocalDefIdMap<ItemLocalId>,
|
||||
|
||||
/// The parent of this node
|
||||
parent_node: hir::ItemLocalId,
|
||||
parent_node: ItemLocalId,
|
||||
|
||||
owner: OwnerId,
|
||||
}
|
||||
@ -31,17 +31,16 @@ pub(super) fn index_hir<'hir>(
|
||||
bodies: &SortedMap<ItemLocalId, &'hir Body<'hir>>,
|
||||
num_nodes: usize,
|
||||
) -> (IndexVec<ItemLocalId, ParentedNode<'hir>>, LocalDefIdMap<ItemLocalId>) {
|
||||
let zero_id = ItemLocalId::ZERO;
|
||||
let err_node = ParentedNode { parent: zero_id, node: Node::Err(item.span()) };
|
||||
let err_node = ParentedNode { parent: ItemLocalId::ZERO, node: Node::Err(item.span()) };
|
||||
let mut nodes = IndexVec::from_elem_n(err_node, num_nodes);
|
||||
// This node's parent should never be accessed: the owner's parent is computed by the
|
||||
// hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
|
||||
// used.
|
||||
nodes[zero_id] = ParentedNode { parent: ItemLocalId::INVALID, node: item.into() };
|
||||
nodes[ItemLocalId::ZERO] = ParentedNode { parent: ItemLocalId::INVALID, node: item.into() };
|
||||
let mut collector = NodeCollector {
|
||||
tcx,
|
||||
owner: item.def_id(),
|
||||
parent_node: zero_id,
|
||||
parent_node: ItemLocalId::ZERO,
|
||||
nodes,
|
||||
bodies,
|
||||
parenting: Default::default(),
|
||||
@ -112,7 +111,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
|
||||
}
|
||||
|
||||
fn insert_nested(&mut self, item: LocalDefId) {
|
||||
if self.parent_node.as_u32() != 0 {
|
||||
if self.parent_node != ItemLocalId::ZERO {
|
||||
self.parenting.insert(item, self.parent_node);
|
||||
}
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
where
|
||||
T: TypeVisitable<TyCtxt<'tcx>>,
|
||||
{
|
||||
t.has_free_regions() || t.has_projections() || t.has_infer_types()
|
||||
t.has_free_regions() || t.has_aliases() || t.has_infer_types()
|
||||
}
|
||||
|
||||
pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "LLVMWrapper.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
|
||||
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@ -103,35 +103,30 @@ fromRust(LLVMRustCounterExprKind Kind) {
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer(
|
||||
const char *const Filenames[],
|
||||
size_t FilenamesLen,
|
||||
const size_t *const Lengths,
|
||||
size_t LengthsLen,
|
||||
const char *const Filenames[], size_t FilenamesLen, // String start pointers
|
||||
const size_t *const Lengths, size_t LengthsLen, // Corresponding lengths
|
||||
RustStringRef BufferOut) {
|
||||
if (FilenamesLen != LengthsLen) {
|
||||
report_fatal_error(
|
||||
"Mismatched lengths in LLVMRustCoverageWriteFilenamesSectionToBuffer");
|
||||
}
|
||||
|
||||
SmallVector<std::string,32> FilenameRefs;
|
||||
SmallVector<std::string, 32> FilenameRefs;
|
||||
FilenameRefs.reserve(FilenamesLen);
|
||||
for (size_t i = 0; i < FilenamesLen; i++) {
|
||||
FilenameRefs.emplace_back(Filenames[i], Lengths[i]);
|
||||
}
|
||||
auto FilenamesWriter =
|
||||
coverage::CoverageFilenamesSectionWriter(ArrayRef<std::string>(FilenameRefs));
|
||||
auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter(
|
||||
ArrayRef<std::string>(FilenameRefs));
|
||||
auto OS = RawRustStringOstream(BufferOut);
|
||||
FilenamesWriter.write(OS);
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustCoverageWriteMappingToBuffer(
|
||||
const unsigned *VirtualFileMappingIDs,
|
||||
unsigned NumVirtualFileMappingIDs,
|
||||
const LLVMRustCounterExpression *RustExpressions,
|
||||
unsigned NumExpressions,
|
||||
const unsigned *VirtualFileMappingIDs, unsigned NumVirtualFileMappingIDs,
|
||||
const LLVMRustCounterExpression *RustExpressions, unsigned NumExpressions,
|
||||
const LLVMRustCounterMappingRegion *RustMappingRegions,
|
||||
unsigned NumMappingRegions,
|
||||
RustStringRef BufferOut) {
|
||||
unsigned NumMappingRegions, RustStringRef BufferOut) {
|
||||
// Convert from FFI representation to LLVM representation.
|
||||
SmallVector<coverage::CounterMappingRegion, 0> MappingRegions;
|
||||
MappingRegions.reserve(NumMappingRegions);
|
||||
@ -142,7 +137,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
|
||||
#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
|
||||
coverage::CounterMappingRegion::MCDCParameters{},
|
||||
#endif
|
||||
Region.FileID, Region.ExpandedFileID,
|
||||
Region.FileID, Region.ExpandedFileID, // File IDs, then region info.
|
||||
Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
|
||||
fromRust(Region.Kind));
|
||||
}
|
||||
@ -158,29 +153,25 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
|
||||
|
||||
auto CoverageMappingWriter = coverage::CoverageMappingWriter(
|
||||
ArrayRef<unsigned>(VirtualFileMappingIDs, NumVirtualFileMappingIDs),
|
||||
Expressions,
|
||||
MappingRegions);
|
||||
Expressions, MappingRegions);
|
||||
auto OS = RawRustStringOstream(BufferOut);
|
||||
CoverageMappingWriter.write(OS);
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(
|
||||
LLVMValueRef F,
|
||||
const char *FuncName,
|
||||
size_t FuncNameLen) {
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName,
|
||||
size_t FuncNameLen) {
|
||||
auto FuncNameRef = StringRef(FuncName, FuncNameLen);
|
||||
return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef));
|
||||
}
|
||||
|
||||
extern "C" uint64_t LLVMRustCoverageHashByteArray(
|
||||
const char *Bytes,
|
||||
size_t NumBytes) {
|
||||
extern "C" uint64_t LLVMRustCoverageHashByteArray(const char *Bytes,
|
||||
size_t NumBytes) {
|
||||
auto StrRef = StringRef(Bytes, NumBytes);
|
||||
return IndexedInstrProf::ComputeHash(StrRef);
|
||||
}
|
||||
|
||||
static void WriteSectionNameToString(LLVMModuleRef M,
|
||||
InstrProfSectKind SK,
|
||||
static void WriteSectionNameToString(LLVMModuleRef M, InstrProfSectKind SK,
|
||||
RustStringRef Str) {
|
||||
auto TargetTriple = Triple(unwrap(M)->getTargetTriple());
|
||||
auto name = getInstrProfSectionName(SK, TargetTriple.getObjectFormat());
|
||||
@ -193,8 +184,9 @@ extern "C" void LLVMRustCoverageWriteMapSectionNameToString(LLVMModuleRef M,
|
||||
WriteSectionNameToString(M, IPSK_covmap, Str);
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustCoverageWriteFuncSectionNameToString(LLVMModuleRef M,
|
||||
RustStringRef Str) {
|
||||
extern "C" void
|
||||
LLVMRustCoverageWriteFuncSectionNameToString(LLVMModuleRef M,
|
||||
RustStringRef Str) {
|
||||
WriteSectionNameToString(M, IPSK_covfun, Str);
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ pub fn provide(providers: &mut Providers) {
|
||||
.parenting
|
||||
.get(&owner_id.def_id)
|
||||
.copied()
|
||||
.unwrap_or(ItemLocalId::from_u32(0)),
|
||||
.unwrap_or(ItemLocalId::ZERO),
|
||||
}
|
||||
})
|
||||
};
|
||||
|
@ -121,8 +121,6 @@ pub enum ProbeStep<'tcx> {
|
||||
/// used whenever there are multiple candidates to prove the
|
||||
/// current goalby .
|
||||
NestedProbe(Probe<'tcx>),
|
||||
CommitIfOkStart,
|
||||
CommitIfOkSuccess,
|
||||
}
|
||||
|
||||
/// What kind of probe we're in. In case the probe represents a candidate, or
|
||||
@ -132,6 +130,8 @@ pub enum ProbeStep<'tcx> {
|
||||
pub enum ProbeKind<'tcx> {
|
||||
/// The root inference context while proving a goal.
|
||||
Root { result: QueryResult<'tcx> },
|
||||
/// Trying to normalize an alias by at least one stpe in `NormalizesTo`.
|
||||
TryNormalizeNonRigid { result: QueryResult<'tcx> },
|
||||
/// Probe entered when normalizing the self ty during candidate assembly
|
||||
NormalizedSelfTyAssembly,
|
||||
/// Some candidate to prove the current goal.
|
||||
@ -143,9 +143,6 @@ pub enum ProbeKind<'tcx> {
|
||||
/// Used in the probe that wraps normalizing the non-self type for the unsize
|
||||
/// trait, which is also structurally matched on.
|
||||
UnsizeAssembly,
|
||||
/// A call to `EvalCtxt::commit_if_ok` which failed, causing the work
|
||||
/// to be discarded.
|
||||
CommitIfOk,
|
||||
/// During upcasting from some source object to target object type, used to
|
||||
/// do a probe to find out what projection type(s) may be used to prove that
|
||||
/// the source type upholds all of the target type's object bounds.
|
||||
|
@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||
ProbeKind::Root { result } => {
|
||||
write!(self.f, "ROOT RESULT: {result:?}")
|
||||
}
|
||||
ProbeKind::TryNormalizeNonRigid { result } => {
|
||||
write!(self.f, "TRY NORMALIZE NON-RIGID: {result:?}")
|
||||
}
|
||||
ProbeKind::NormalizedSelfTyAssembly => {
|
||||
write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
|
||||
}
|
||||
@ -109,9 +112,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||
ProbeKind::UpcastProjectionCompatibility => {
|
||||
write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
|
||||
}
|
||||
ProbeKind::CommitIfOk => {
|
||||
write!(self.f, "COMMIT_IF_OK:")
|
||||
}
|
||||
ProbeKind::MiscCandidate { name, result } => {
|
||||
write!(self.f, "CANDIDATE {name}: {result:?}")
|
||||
}
|
||||
@ -132,8 +132,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||
}
|
||||
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
|
||||
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
|
||||
ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?,
|
||||
ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -49,7 +49,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
let value = self.erase_regions(value);
|
||||
debug!(?value);
|
||||
|
||||
if !value.has_projections() {
|
||||
if !value.has_aliases() {
|
||||
value
|
||||
} else {
|
||||
value.fold_with(&mut NormalizeAfterErasingRegionsFolder { tcx: self, param_env })
|
||||
@ -81,7 +81,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
let value = self.erase_regions(value);
|
||||
debug!(?value);
|
||||
|
||||
if !value.has_projections() {
|
||||
if !value.has_aliases() {
|
||||
Ok(value)
|
||||
} else {
|
||||
let mut folder = TryNormalizeAfterErasingRegionsFolder::new(self, param_env);
|
||||
|
@ -1077,7 +1077,7 @@ fn try_instance_mir<'tcx>(
|
||||
let fields = def.all_fields();
|
||||
for field in fields {
|
||||
let field_ty = field.ty(tcx, args);
|
||||
if field_ty.has_param() && field_ty.has_projections() {
|
||||
if field_ty.has_param() && field_ty.has_aliases() {
|
||||
return Err("cannot build drop shim for polymorphic type");
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
//! Doing this via a separate goal is called "deferred alias relation" and part
|
||||
//! of our more general approach to "lazy normalization".
|
||||
//!
|
||||
//! This is done by first normalizing both sides of the goal, ending up in
|
||||
//! either a concrete type, rigid alias, or an infer variable.
|
||||
//! This is done by first structurally normalizing both sides of the goal, ending
|
||||
//! up in either a concrete type, rigid alias, or an infer variable.
|
||||
//! These are related further according to the rules below:
|
||||
//!
|
||||
//! (1.) If we end up with two rigid aliases, then we relate them structurally.
|
||||
@ -14,18 +14,10 @@
|
||||
//!
|
||||
//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
|
||||
//! relate them structurally.
|
||||
//!
|
||||
//! Subtle: when relating an opaque to another type, we emit a
|
||||
//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque.
|
||||
//! This nested goal starts out as ambiguous and does not actually define the opaque.
|
||||
//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
|
||||
//! `NormalizesTo` goal, at which point the opaque is actually defined.
|
||||
|
||||
use super::EvalCtxt;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::solve::GoalSource;
|
||||
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty;
|
||||
|
||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
@ -36,21 +28,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
let tcx = self.tcx();
|
||||
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
|
||||
|
||||
let Some(lhs) = self.try_normalize_term(param_env, lhs)? else {
|
||||
return self
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
||||
// Structurally normalize the lhs.
|
||||
let lhs = if let Some(alias) = lhs.to_alias_ty(self.tcx()) {
|
||||
let term = self.next_term_infer_of_kind(lhs);
|
||||
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||
term
|
||||
} else {
|
||||
lhs
|
||||
};
|
||||
|
||||
let Some(rhs) = self.try_normalize_term(param_env, rhs)? else {
|
||||
return self
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
||||
// Structurally normalize the rhs.
|
||||
let rhs = if let Some(alias) = rhs.to_alias_ty(self.tcx()) {
|
||||
let term = self.next_term_infer_of_kind(rhs);
|
||||
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||
term
|
||||
} else {
|
||||
rhs
|
||||
};
|
||||
|
||||
// Apply the constraints.
|
||||
self.try_evaluate_added_goals()?;
|
||||
let lhs = self.resolve_vars_if_possible(lhs);
|
||||
let rhs = self.resolve_vars_if_possible(rhs);
|
||||
debug!(?lhs, ?rhs);
|
||||
|
||||
let variance = match direction {
|
||||
ty::AliasRelationDirection::Equate => ty::Variance::Invariant,
|
||||
ty::AliasRelationDirection::Subtype => ty::Variance::Covariant,
|
||||
};
|
||||
|
||||
match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
|
||||
(None, None) => {
|
||||
self.relate(param_env, lhs, variance, rhs)?;
|
||||
@ -58,14 +63,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
|
||||
(Some(alias), None) => {
|
||||
self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)
|
||||
self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
(None, Some(alias)) => {
|
||||
self.relate_rigid_alias_non_alias(
|
||||
param_env,
|
||||
alias,
|
||||
variance.xform(ty::Variance::Contravariant),
|
||||
lhs,
|
||||
)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
(None, Some(alias)) => self.relate_rigid_alias_non_alias(
|
||||
param_env,
|
||||
alias,
|
||||
variance.xform(ty::Variance::Contravariant),
|
||||
lhs,
|
||||
),
|
||||
|
||||
(Some(alias_lhs), Some(alias_rhs)) => {
|
||||
self.relate(param_env, alias_lhs, variance, alias_rhs)?;
|
||||
@ -73,104 +82,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Relate a rigid alias with another type. This is the same as
|
||||
/// an ordinary relate except that we treat the outer most alias
|
||||
/// constructor as rigid.
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
fn relate_rigid_alias_non_alias(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
alias: ty::AliasTy<'tcx>,
|
||||
variance: ty::Variance,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
// NOTE: this check is purely an optimization, the structural eq would
|
||||
// always fail if the term is not an inference variable.
|
||||
if term.is_infer() {
|
||||
let tcx = self.tcx();
|
||||
// We need to relate `alias` to `term` treating only the outermost
|
||||
// constructor as rigid, relating any contained generic arguments as
|
||||
// normal. We do this by first structurally equating the `term`
|
||||
// with the alias constructor instantiated with unconstrained infer vars,
|
||||
// and then relate this with the whole `alias`.
|
||||
//
|
||||
// Alternatively we could modify `Equate` for this case by adding another
|
||||
// variant to `StructurallyRelateAliases`.
|
||||
let identity_args = self.fresh_args_for_item(alias.def_id);
|
||||
let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args);
|
||||
self.eq_structurally_relating_aliases(param_env, term, rigid_ctor.to_ty(tcx).into())?;
|
||||
self.eq(param_env, alias, rigid_ctor)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var.
|
||||
/// Normalize the `term` to equate it later.
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
fn try_normalize_term(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> Result<Option<ty::Term<'tcx>>, NoSolution> {
|
||||
match term.unpack() {
|
||||
ty::TermKind::Ty(ty) => {
|
||||
Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into))
|
||||
}
|
||||
ty::TermKind::Const(_) => {
|
||||
if let Some(alias) = term.to_alias_ty(self.tcx()) {
|
||||
let term = self.next_term_infer_of_kind(term);
|
||||
self.add_normalizes_to_goal(Goal::new(
|
||||
self.tcx(),
|
||||
param_env,
|
||||
ty::NormalizesTo { alias, term },
|
||||
));
|
||||
self.try_evaluate_added_goals()?;
|
||||
Ok(Some(self.resolve_vars_if_possible(term)))
|
||||
} else {
|
||||
Ok(Some(term))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
fn try_normalize_ty_recur(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
depth: usize,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if !self.tcx().recursion_limit().value_within_limit(depth) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ty::Alias(kind, alias) = *ty.kind() else {
|
||||
return Some(ty);
|
||||
};
|
||||
|
||||
match self.commit_if_ok(|this| {
|
||||
let tcx = this.tcx();
|
||||
let normalized_ty = this.next_ty_infer();
|
||||
let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() };
|
||||
match kind {
|
||||
ty::AliasKind::Opaque => {
|
||||
// HACK: Unlike for associated types, `normalizes-to` for opaques
|
||||
// is currently not treated as a function. We do not erase the
|
||||
// expected term.
|
||||
this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to));
|
||||
}
|
||||
ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => {
|
||||
this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to))
|
||||
}
|
||||
}
|
||||
this.try_evaluate_added_goals()?;
|
||||
Ok(this.resolve_vars_if_possible(normalized_ty))
|
||||
}) {
|
||||
Ok(ty) => self.try_normalize_ty_recur(param_env, depth + 1, ty),
|
||||
Err(NoSolution) => Some(ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -312,11 +312,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec<Candidate<'tcx>> {
|
||||
let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
|
||||
let certainty = Certainty::Maybe(cause);
|
||||
let result = self.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
|
||||
// This may fail if `try_evaluate_added_goals` overflows because it
|
||||
// fails to reach a fixpoint but ends up getting an error after
|
||||
// running for some additional step.
|
||||
//
|
||||
// FIXME: Add a test for this. It seems to be necessary for typenum but
|
||||
// is incredibly hard to minimize as it may rely on being inside of a
|
||||
// trait solver cycle.
|
||||
let result = self.evaluate_added_goals_and_make_canonical_response(certainty);
|
||||
let mut dummy_probe = self.inspect.new_probe();
|
||||
dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
|
||||
dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result });
|
||||
self.inspect.finish_probe(dummy_probe);
|
||||
vec![Candidate { source, result }]
|
||||
if let Ok(result) = result { vec![Candidate { source, result }] } else { vec![] }
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
|
@ -332,7 +332,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
/// whether an alias is rigid by using the trait solver. When instantiating a response
|
||||
/// from the solver we assume that the solver correctly handled aliases and therefore
|
||||
/// always relate them structurally here.
|
||||
#[instrument(level = "debug", skip(infcx), ret)]
|
||||
#[instrument(level = "debug", skip(infcx))]
|
||||
fn unify_query_var_values(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
@ -1,47 +0,0 @@
|
||||
use super::{EvalCtxt, NestedGoals};
|
||||
use crate::solve::inspect;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
|
||||
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
pub(in crate::solve) fn commit_if_ok<T>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>,
|
||||
) -> Result<T, NoSolution> {
|
||||
let mut nested_ecx = EvalCtxt {
|
||||
infcx: self.infcx,
|
||||
variables: self.variables,
|
||||
var_values: self.var_values,
|
||||
is_normalizes_to_goal: self.is_normalizes_to_goal,
|
||||
predefined_opaques_in_body: self.predefined_opaques_in_body,
|
||||
max_input_universe: self.max_input_universe,
|
||||
search_graph: self.search_graph,
|
||||
nested_goals: NestedGoals::new(),
|
||||
tainted: self.tainted,
|
||||
inspect: self.inspect.new_probe(),
|
||||
};
|
||||
|
||||
let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx));
|
||||
if result.is_ok() {
|
||||
let EvalCtxt {
|
||||
infcx: _,
|
||||
variables: _,
|
||||
var_values: _,
|
||||
is_normalizes_to_goal: _,
|
||||
predefined_opaques_in_body: _,
|
||||
max_input_universe: _,
|
||||
search_graph: _,
|
||||
nested_goals,
|
||||
tainted,
|
||||
inspect,
|
||||
} = nested_ecx;
|
||||
self.nested_goals.extend(nested_goals);
|
||||
self.tainted = tainted;
|
||||
self.inspect.integrate_snapshot(inspect);
|
||||
} else {
|
||||
nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk);
|
||||
self.inspect.finish_probe(nested_ecx.inspect);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@ use rustc_middle::ty::{
|
||||
use rustc_session::config::DumpSolverProofTree;
|
||||
use rustc_span::DUMMY_SP;
|
||||
use std::io::Write;
|
||||
use std::iter;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
||||
@ -36,7 +35,6 @@ use super::{GoalSource, SolverMode};
|
||||
pub use select::InferCtxtSelectExt;
|
||||
|
||||
mod canonical;
|
||||
mod commit_if_ok;
|
||||
mod probe;
|
||||
mod select;
|
||||
|
||||
@ -124,11 +122,6 @@ impl<'tcx> NestedGoals<'tcx> {
|
||||
pub(super) fn is_empty(&self) -> bool {
|
||||
self.normalizes_to_goals.is_empty() && self.goals.is_empty()
|
||||
}
|
||||
|
||||
pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) {
|
||||
self.normalizes_to_goals.extend(other.normalizes_to_goals);
|
||||
self.goals.extend(other.goals)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||
@ -511,12 +504,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
|
||||
self.inspect.evaluate_added_goals_loop_start();
|
||||
|
||||
fn with_misc_source<'tcx>(
|
||||
it: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) -> impl Iterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)> {
|
||||
iter::zip(iter::repeat(GoalSource::Misc), it)
|
||||
}
|
||||
|
||||
// If this loop did not result in any progress, what's our final certainty.
|
||||
let mut unchanged_certainty = Some(Certainty::Yes);
|
||||
for goal in goals.normalizes_to_goals {
|
||||
@ -534,16 +521,28 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
unconstrained_goal,
|
||||
)?;
|
||||
// Add the nested goals from normalization to our own nested goals.
|
||||
debug!(?nested_goals);
|
||||
goals.goals.extend(nested_goals);
|
||||
|
||||
// Finally, equate the goal's RHS with the unconstrained var.
|
||||
// We put the nested goals from this into goals instead of
|
||||
// next_goals to avoid needing to process the loop one extra
|
||||
// time if this goal returns something -- I don't think this
|
||||
// matters in practice, though.
|
||||
let eq_goals =
|
||||
self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?;
|
||||
goals.goals.extend(with_misc_source(eq_goals));
|
||||
//
|
||||
// SUBTLE:
|
||||
// We structurally relate aliases here. This is necessary
|
||||
// as we otherwise emit a nested `AliasRelate` goal in case the
|
||||
// returned term is a rigid alias, resulting in overflow.
|
||||
//
|
||||
// It is correct as both `goal.predicate.term` and `unconstrained_rhs`
|
||||
// start out as an unconstrained inference variable so any aliases get
|
||||
// fully normalized when instantiating it.
|
||||
//
|
||||
// FIXME: Strictly speaking this may be incomplete if the normalized-to
|
||||
// type contains an ambiguous alias referencing bound regions. We should
|
||||
// consider changing this to only use "shallow structural equality".
|
||||
self.eq_structurally_relating_aliases(
|
||||
goal.param_env,
|
||||
goal.predicate.term,
|
||||
unconstrained_rhs,
|
||||
)?;
|
||||
|
||||
// We only look at the `projection_ty` part here rather than
|
||||
// looking at the "has changed" return from evaluate_goal,
|
||||
@ -731,6 +730,46 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// This should be used when relating a rigid alias with another type.
|
||||
///
|
||||
/// Normally we emit a nested `AliasRelate` when equating an inference
|
||||
/// variable and an alias. This causes us to instead constrain the inference
|
||||
/// variable to the alias without emitting a nested alias relate goals.
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
pub(super) fn relate_rigid_alias_non_alias(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
alias: ty::AliasTy<'tcx>,
|
||||
variance: ty::Variance,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> Result<(), NoSolution> {
|
||||
// NOTE: this check is purely an optimization, the structural eq would
|
||||
// always fail if the term is not an inference variable.
|
||||
if term.is_infer() {
|
||||
let tcx = self.tcx();
|
||||
// We need to relate `alias` to `term` treating only the outermost
|
||||
// constructor as rigid, relating any contained generic arguments as
|
||||
// normal. We do this by first structurally equating the `term`
|
||||
// with the alias constructor instantiated with unconstrained infer vars,
|
||||
// and then relate this with the whole `alias`.
|
||||
//
|
||||
// Alternatively we could modify `Equate` for this case by adding another
|
||||
// variant to `StructurallyRelateAliases`.
|
||||
let identity_args = self.fresh_args_for_item(alias.def_id);
|
||||
let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args);
|
||||
let ctor_ty = rigid_ctor.to_ty(tcx);
|
||||
let InferOk { value: (), obligations } = self
|
||||
.infcx
|
||||
.at(&ObligationCause::dummy(), param_env)
|
||||
.trace(term, ctor_ty.into())
|
||||
.eq_structurally_relating_aliases(term, ctor_ty.into())?;
|
||||
debug_assert!(obligations.is_empty());
|
||||
self.relate(param_env, alias, variance, rigid_ctor)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
/// This sohuld only be used when we're either instantiating a previously
|
||||
/// unconstrained "return value" or when we're sure that all aliases in
|
||||
/// the types are rigid.
|
||||
|
@ -130,17 +130,14 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
||||
self.candidates_recur(candidates, nested_goals, probe);
|
||||
nested_goals.truncate(num_goals);
|
||||
}
|
||||
inspect::ProbeStep::EvaluateGoals(_)
|
||||
| inspect::ProbeStep::CommitIfOkStart
|
||||
| inspect::ProbeStep::CommitIfOkSuccess => (),
|
||||
inspect::ProbeStep::EvaluateGoals(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
match probe.kind {
|
||||
inspect::ProbeKind::NormalizedSelfTyAssembly
|
||||
| inspect::ProbeKind::UnsizeAssembly
|
||||
| inspect::ProbeKind::UpcastProjectionCompatibility
|
||||
| inspect::ProbeKind::CommitIfOk => (),
|
||||
| inspect::ProbeKind::UpcastProjectionCompatibility => (),
|
||||
// We add a candidate for the root evaluation if there
|
||||
// is only one way to prove a given goal, e.g. for `WellFormed`.
|
||||
//
|
||||
@ -157,7 +154,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
||||
});
|
||||
}
|
||||
}
|
||||
inspect::ProbeKind::MiscCandidate { name: _, result }
|
||||
inspect::ProbeKind::TryNormalizeNonRigid { result }
|
||||
| inspect::ProbeKind::MiscCandidate { name: _, result }
|
||||
| inspect::ProbeKind::TraitCandidate { source: _, result } => {
|
||||
candidates.push(InspectCandidate {
|
||||
goal: self,
|
||||
|
@ -220,8 +220,6 @@ enum WipProbeStep<'tcx> {
|
||||
AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
|
||||
EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
|
||||
NestedProbe(WipProbe<'tcx>),
|
||||
CommitIfOkStart,
|
||||
CommitIfOkSuccess,
|
||||
}
|
||||
|
||||
impl<'tcx> WipProbeStep<'tcx> {
|
||||
@ -230,8 +228,6 @@ impl<'tcx> WipProbeStep<'tcx> {
|
||||
WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal),
|
||||
WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
|
||||
WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
|
||||
WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart,
|
||||
WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -467,29 +463,6 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside
|
||||
/// of the probe into the parent.
|
||||
pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
match (this, *probe.state.unwrap()) {
|
||||
(
|
||||
DebugSolver::Probe(WipProbe { steps, .. })
|
||||
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
|
||||
evaluation: WipProbe { steps, .. },
|
||||
..
|
||||
}),
|
||||
DebugSolver::Probe(probe),
|
||||
) => {
|
||||
steps.push(WipProbeStep::CommitIfOkStart);
|
||||
assert_eq!(probe.kind, None);
|
||||
steps.extend(probe.steps);
|
||||
steps.push(WipProbeStep::CommitIfOkSuccess);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
|
||||
self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None })
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
|
||||
fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
|
||||
let infcx = self.at.infcx;
|
||||
debug_assert_eq!(ty, infcx.shallow_resolve(ty));
|
||||
if !ty.has_projections() {
|
||||
if !ty.has_aliases() {
|
||||
return Ok(ty);
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
|
||||
fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
|
||||
let infcx = self.at.infcx;
|
||||
debug_assert_eq!(ct, infcx.shallow_resolve(ct));
|
||||
if !ct.has_projections() {
|
||||
if !ct.has_aliases() {
|
||||
return Ok(ct);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::solve::inspect::ProbeKind;
|
||||
use rustc_infer::traits::specialization_graph::LeafDef;
|
||||
use rustc_infer::traits::Reveal;
|
||||
use rustc_middle::traits::solve::{
|
||||
@ -30,14 +31,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
&mut self,
|
||||
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||
) -> QueryResult<'tcx> {
|
||||
let def_id = goal.predicate.def_id();
|
||||
let def_kind = self.tcx().def_kind(def_id);
|
||||
match def_kind {
|
||||
DefKind::OpaqueTy => return self.normalize_opaque_type(goal),
|
||||
_ => self.set_is_normalizes_to_goal(),
|
||||
}
|
||||
|
||||
self.set_is_normalizes_to_goal();
|
||||
debug_assert!(self.term_is_fully_unconstrained(goal));
|
||||
let normalize_result = self
|
||||
.probe(|&result| ProbeKind::TryNormalizeNonRigid { result })
|
||||
.enter(|this| this.normalize_at_least_one_step(goal));
|
||||
|
||||
match normalize_result {
|
||||
Ok(res) => Ok(res),
|
||||
Err(NoSolution) => {
|
||||
let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
|
||||
if alias.opt_kind(self.tcx()).is_some() {
|
||||
self.relate_rigid_alias_non_alias(
|
||||
param_env,
|
||||
alias,
|
||||
ty::Variance::Invariant,
|
||||
term,
|
||||
)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
// FIXME(generic_const_exprs): we currently do not support rigid
|
||||
// unevaluated constants.
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalize the given alias by at least one step. If the alias is rigid, this
|
||||
/// returns `NoSolution`.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn normalize_at_least_one_step(
|
||||
&mut self,
|
||||
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||
) -> QueryResult<'tcx> {
|
||||
let def_id = goal.predicate.def_id();
|
||||
match self.tcx().def_kind(def_id) {
|
||||
DefKind::AssocTy | DefKind::AssocConst => {
|
||||
match self.tcx().associated_item(def_id).container {
|
||||
@ -52,35 +80,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
DefKind::AnonConst => self.normalize_anon_const(goal),
|
||||
DefKind::TyAlias => self.normalize_weak_type(goal),
|
||||
DefKind::OpaqueTy => self.normalize_opaque_type(goal),
|
||||
kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)),
|
||||
}
|
||||
}
|
||||
|
||||
/// When normalizing an associated item, constrain the result to `term`.
|
||||
/// When normalizing an associated item, constrain the expected term to `term`.
|
||||
///
|
||||
/// While `NormalizesTo` goals have the normalized-to term as an argument,
|
||||
/// this argument is always fully unconstrained for associated items.
|
||||
/// It is therefore appropriate to instead think of these `NormalizesTo` goals
|
||||
/// as function returning a term after normalizing.
|
||||
///
|
||||
/// When equating an inference variable and an alias, we tend to emit `alias-relate`
|
||||
/// goals and only actually instantiate the inference variable with an alias if the
|
||||
/// alias is rigid. However, this means that constraining the expected term of
|
||||
/// such goals ends up fully structurally normalizing the resulting type instead of
|
||||
/// only by one step. To avoid this we instead use structural equality here, resulting
|
||||
/// in each `NormalizesTo` only projects by a single step.
|
||||
///
|
||||
/// Not doing so, currently causes issues because trying to normalize an opaque type
|
||||
/// during alias-relate doesn't actually constrain the opaque if the concrete type
|
||||
/// is an inference variable. This means that `NormalizesTo` for associated types
|
||||
/// normalizing to an opaque type always resulted in ambiguity, breaking tests e.g.
|
||||
/// tests/ui/type-alias-impl-trait/issue-78450.rs.
|
||||
/// We know `term` to always be a fully unconstrained inference variable, so
|
||||
/// `eq` should never fail here. However, in case `term` contains aliases, we
|
||||
/// emit nested `AliasRelate` goals to structurally normalize the alias.
|
||||
pub fn instantiate_normalizes_to_term(
|
||||
&mut self,
|
||||
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||
term: ty::Term<'tcx>,
|
||||
) {
|
||||
self.eq_structurally_relating_aliases(goal.param_env, goal.predicate.term, term)
|
||||
self.eq(goal.param_env, goal.predicate.term, term)
|
||||
.expect("expected goal term to be fully unconstrained");
|
||||
}
|
||||
}
|
||||
|
@ -58,12 +58,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
let expected = self.structurally_normalize_ty(goal.param_env, expected)?;
|
||||
if expected.is_ty_var() {
|
||||
return self
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
||||
}
|
||||
|
||||
// Otherwise, define a new opaque type
|
||||
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
|
||||
self.add_item_bounds_for_hidden_type(
|
||||
|
@ -311,7 +311,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
|
||||
|
||||
let infcx = self.selcx.infcx;
|
||||
|
||||
if obligation.predicate.has_projections() {
|
||||
if obligation.predicate.has_aliases() {
|
||||
let mut obligations = Vec::new();
|
||||
let predicate = normalize_with_depth_to(
|
||||
&mut self.selcx,
|
||||
|
@ -101,6 +101,8 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||
value: &T,
|
||||
reveal: Reveal,
|
||||
) -> bool {
|
||||
// This mirrors `ty::TypeFlags::HAS_ALIASES` except that we take `Reveal` into account.
|
||||
|
||||
let mut flags = ty::TypeFlags::HAS_TY_PROJECTION
|
||||
| ty::TypeFlags::HAS_TY_WEAK
|
||||
| ty::TypeFlags::HAS_TY_INHERENT
|
||||
|
@ -431,7 +431,7 @@ pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
|
||||
let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term);
|
||||
|
||||
let mut result = if projected_term.has_projections() {
|
||||
let mut result = if projected_term.has_aliases() {
|
||||
let normalized_ty = normalize_with_depth_to(
|
||||
selcx,
|
||||
param_env,
|
||||
@ -595,7 +595,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
|
||||
let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args);
|
||||
|
||||
let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
|
||||
if ty.has_projections() {
|
||||
if ty.has_aliases() {
|
||||
ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ where
|
||||
type QueryResponse = T;
|
||||
|
||||
fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<T> {
|
||||
if !key.value.value.has_projections() { Some(key.value.value) } else { None }
|
||||
if !key.value.value.has_aliases() { Some(key.value.value) } else { None }
|
||||
}
|
||||
|
||||
fn perform_query(
|
||||
|
@ -1060,7 +1060,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// so we will try to normalize the obligation and evaluate again.
|
||||
// we will replace it with new solver in the future.
|
||||
if EvaluationResult::EvaluatedToErr == result
|
||||
&& fresh_trait_pred.has_projections()
|
||||
&& fresh_trait_pred.has_aliases()
|
||||
&& fresh_trait_pred.is_global()
|
||||
{
|
||||
let mut nested_obligations = Vec::new();
|
||||
|
@ -241,9 +241,9 @@ fn layout_of_uncached<'tcx>(
|
||||
|
||||
// Arrays and slices.
|
||||
ty::Array(element, mut count) => {
|
||||
if count.has_projections() {
|
||||
if count.has_aliases() {
|
||||
count = tcx.normalize_erasing_regions(param_env, count);
|
||||
if count.has_projections() {
|
||||
if count.has_aliases() {
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
}
|
||||
|
@ -78,8 +78,10 @@ bitflags! {
|
||||
/// Does this have `ConstKind::Unevaluated`?
|
||||
const HAS_CT_PROJECTION = 1 << 14;
|
||||
|
||||
/// Could this type be normalized further?
|
||||
const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits()
|
||||
/// Does this have `Alias` or `ConstKind::Unevaluated`?
|
||||
///
|
||||
/// Rephrased, could this term be normalized further?
|
||||
const HAS_ALIASES = TypeFlags::HAS_TY_PROJECTION.bits()
|
||||
| TypeFlags::HAS_TY_WEAK.bits()
|
||||
| TypeFlags::HAS_TY_OPAQUE.bits()
|
||||
| TypeFlags::HAS_TY_INHERENT.bits()
|
||||
|
@ -223,8 +223,8 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
|
||||
self.has_vars_bound_at_or_above(ty::INNERMOST)
|
||||
}
|
||||
|
||||
fn has_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PROJECTION)
|
||||
fn has_aliases(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_ALIASES)
|
||||
}
|
||||
|
||||
fn has_inherent_projections(&self) -> bool {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::fmt;
|
||||
use crate::hash::{Hash, Hasher};
|
||||
use crate::marker::Freeze;
|
||||
|
||||
/// Provides the pointer metadata type of any pointed-to type.
|
||||
///
|
||||
@ -57,7 +58,7 @@ pub trait Pointee {
|
||||
// NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata`
|
||||
// in `library/core/src/ptr/metadata.rs`
|
||||
// in sync with those here:
|
||||
type Metadata: fmt::Debug + Copy + Send + Sync + Ord + Hash + Unpin;
|
||||
type Metadata: fmt::Debug + Copy + Send + Sync + Ord + Hash + Unpin + Freeze;
|
||||
}
|
||||
|
||||
/// Pointers to types implementing this trait alias are “thin”.
|
||||
|
@ -38,6 +38,7 @@
|
||||
#![feature(duration_constructors)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(extern_types)]
|
||||
#![feature(freeze)]
|
||||
#![feature(flt2dec)]
|
||||
#![feature(fmt_internals)]
|
||||
#![feature(float_minimum_maximum)]
|
||||
|
@ -1,4 +1,5 @@
|
||||
use core::cell::RefCell;
|
||||
use core::marker::Freeze;
|
||||
use core::mem::{self, MaybeUninit};
|
||||
use core::num::NonZero;
|
||||
use core::ptr;
|
||||
@ -841,7 +842,7 @@ fn ptr_metadata_bounds() {
|
||||
fn static_assert_expected_bounds_for_metadata<Meta>()
|
||||
where
|
||||
// Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs`
|
||||
Meta: Debug + Copy + Send + Sync + Ord + std::hash::Hash + Unpin,
|
||||
Meta: Debug + Copy + Send + Sync + Ord + std::hash::Hash + Unpin + Freeze,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
0.17.0
|
||||
0.17.1
|
@ -34,6 +34,9 @@ the main page is located in `doc/lib/index.html`. If you open that up in
|
||||
a web browser, you will see a page with a search bar, and "Crate lib" at the
|
||||
top, with no contents.
|
||||
|
||||
You can also use `cargo doc` to generate documentation for the whole project.
|
||||
See [Using rustdoc with Cargo](#using-rustdoc-with-cargo).
|
||||
|
||||
## Configuring rustdoc
|
||||
|
||||
There are two problems with this: first, why does it
|
||||
@ -79,7 +82,13 @@ docs. Instead of the `rustdoc` command, we could have done this:
|
||||
$ cargo doc
|
||||
```
|
||||
|
||||
Internally, this calls out to `rustdoc` like this:
|
||||
If you want `cargo` to automatically open the generated documentation, you can use:
|
||||
|
||||
```bash
|
||||
$ cargo doc --open
|
||||
```
|
||||
|
||||
Internally, `cargo doc` calls out to `rustdoc` like this:
|
||||
|
||||
```bash
|
||||
$ rustdoc --crate-name docs src/lib.rs -o <path>/docs/target/doc -L
|
||||
|
@ -34,7 +34,7 @@ nav.sub {
|
||||
in rustdoc.css */
|
||||
|
||||
/* Begin theme: light */
|
||||
:root {
|
||||
:root, :root:not([data-theme]) {
|
||||
--main-background-color: white;
|
||||
--main-color: black;
|
||||
--settings-input-color: #2196f3;
|
||||
@ -140,7 +140,7 @@ nav.sub {
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* Begin theme: dark */
|
||||
:root {
|
||||
:root, :root:not([data-theme]) {
|
||||
--main-background-color: #353535;
|
||||
--main-color: #ddd;
|
||||
--settings-input-color: #2196f3;
|
||||
|
@ -2315,8 +2315,14 @@ in src-script.js and main.js
|
||||
tooling to ensure different themes all define all the variables. Do not
|
||||
alter their formatting. */
|
||||
|
||||
/*
|
||||
About `:root:not([data-theme])`: if for any reason the JS is enabled but cannot be loaded,
|
||||
`noscript` won't be enabled and the doc will have no color applied. To do around this, we
|
||||
add a selector check that if `data-theme` is not defined, then we apply the light theme
|
||||
by default.
|
||||
*/
|
||||
/* Begin theme: light */
|
||||
:root[data-theme="light"] {
|
||||
:root[data-theme="light"], :root:not([data-theme]) {
|
||||
--main-background-color: white;
|
||||
--main-color: black;
|
||||
--settings-input-color: #2196f3;
|
||||
|
@ -74,8 +74,11 @@ fn compare_themes<'a>(
|
||||
(noscript_css_line_number, noscript_css_line),
|
||||
) in rustdoc_css_lines.zip(noscript_css_lines)
|
||||
{
|
||||
if noscript_css_line.starts_with(":root {")
|
||||
&& rustdoc_css_line.starts_with(&format!(r#":root[data-theme="{name}"] {{"#))
|
||||
if noscript_css_line.starts_with(":root, :root:not([data-theme]) {")
|
||||
&& (rustdoc_css_line.starts_with(&format!(r#":root[data-theme="{name}"] {{"#))
|
||||
|| rustdoc_css_line.starts_with(&format!(
|
||||
r#":root[data-theme="{name}"], :root:not([data-theme]) {{"#
|
||||
)))
|
||||
{
|
||||
// selectors are different between rustdoc.css and noscript.css
|
||||
// that's why they both exist: one uses JS, the other uses media queries
|
||||
|
@ -3,4 +3,18 @@
|
||||
javascript: false
|
||||
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
|
||||
show-text: true
|
||||
assert-css: (".sub", {"display": "none"})
|
||||
|
||||
// Even though JS is disabled, we should still have themes applied. Links are never black-colored
|
||||
// if styles are applied so we check that they are not.
|
||||
assert-css-false: ("a.src", {"color": "#000"})
|
||||
|
||||
javascript: true
|
||||
fail-on-request-error: false
|
||||
block-network-request: "*.js"
|
||||
reload:
|
||||
|
||||
// JS is enabled but wasn't loaded, we should still have the light theme applied. Links are never
|
||||
// black-colored if styles are applied so we check that they are not.
|
||||
assert-css-false: ("a.src", {"color": "#000"})
|
||||
|
@ -6,7 +6,7 @@ LL | cmp_eq
|
||||
|
|
||||
= note: cannot satisfy `_: Scalar`
|
||||
note: required by a bound in `cmp_eq`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:9:22
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:10:22
|
||||
|
|
||||
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
||||
| ^^^^^^ required by this bound in `cmp_eq`
|
||||
@ -15,34 +15,6 @@ help: consider specifying the generic arguments
|
||||
LL | cmp_eq::<A, B, O>
|
||||
| +++++++++++
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
|
||||
|
|
||||
LL | cmp_eq
|
||||
| ^^^^^^
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
|
||||
|
|
||||
LL | cmp_eq
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::<O, ..., ...>} <: ...`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:14:51
|
||||
|
|
||||
LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
||||
| ___________________________________________________^
|
||||
LL | |
|
||||
LL | | cmp_eq
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0275, E0283.
|
||||
For more information about an error, try `rustc --explain E0275`.
|
||||
For more information about this error, try `rustc --explain E0283`.
|
||||
|
@ -6,7 +6,7 @@ LL | cmp_eq
|
||||
|
|
||||
= note: cannot satisfy `_: Scalar`
|
||||
note: required by a bound in `cmp_eq`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:9:22
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:10:22
|
||||
|
|
||||
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
||||
| ^^^^^^ required by this bound in `cmp_eq`
|
||||
|
@ -1,4 +1,5 @@
|
||||
//@ revisions: old next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
pub trait Scalar: 'static {
|
||||
type RefType<'a>: ScalarRef<'a>;
|
||||
@ -12,11 +13,8 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT
|
||||
|
||||
fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
|
||||
) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
||||
//[next]~^ ERROR overflow evaluating the requirement
|
||||
cmp_eq
|
||||
//~^ ERROR type annotations needed
|
||||
//[next]~| ERROR overflow evaluating the requirement
|
||||
//[next]~| ERROR overflow evaluating the requirement
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/recursive-coroutine-boxed.rs:12:23
|
||||
--> $DIR/recursive-coroutine-boxed.rs:14:23
|
||||
|
|
||||
LL | let mut gen = Box::pin(foo());
|
||||
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
|
||||
@ -12,12 +12,28 @@ help: consider specifying the generic argument
|
||||
LL | let mut gen = Box::<T>::pin(foo());
|
||||
| +++++
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/recursive-coroutine-boxed.rs:9:13
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/recursive-coroutine-boxed.rs:13:5
|
||||
|
|
||||
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
|
||||
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
||||
| ---------------------------------------
|
||||
| |
|
||||
| the expected opaque type
|
||||
| expected `impl Coroutine<Yield = (), Return = ()>` because of return type
|
||||
...
|
||||
LL | / || {
|
||||
LL | | let mut gen = Box::pin(foo());
|
||||
LL | |
|
||||
LL | | let mut r = gen.as_mut().resume(());
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_____^ types differ
|
||||
|
|
||||
= note: expected opaque type `impl Coroutine<Yield = (), Return = ()>`
|
||||
found coroutine `{coroutine@$DIR/recursive-coroutine-boxed.rs:13:5: 13:7}`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0282`.
|
||||
Some errors have detailed explanations: E0282, E0308.
|
||||
For more information about an error, try `rustc --explain E0282`.
|
||||
|
@ -7,8 +7,10 @@
|
||||
use std::ops::{Coroutine, CoroutineState};
|
||||
|
||||
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
||||
//[next]~^ ERROR type annotations needed
|
||||
|| {
|
||||
// FIXME(-Znext-solver): this fails with a mismatched types as the
|
||||
// hidden type of the opaque ends up as {type error}. We should not
|
||||
// emit errors for such goals.
|
||||
|| { //[next]~ ERROR mismatched types
|
||||
let mut gen = Box::pin(foo());
|
||||
//[next]~^ ERROR type annotations needed
|
||||
let mut r = gen.as_mut().resume(());
|
||||
|
@ -77,3 +77,14 @@ fn closure_addr_taken() {
|
||||
let call = Fn::<()>::call;
|
||||
use_closure(call, &f);
|
||||
}
|
||||
|
||||
fn use_closure_once<C>(call: extern "rust-call" fn(C, ()) -> i32, f: C) -> i32 {
|
||||
call(f, ())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_once_addr_taken() {
|
||||
let g = || 3;
|
||||
let call2 = FnOnce::<()>::call_once;
|
||||
use_closure_once(call2, g);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user