diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 402044c7af9..1c34fd0afbb 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -19,7 +19,7 @@ struct NodeCollector<'a, 'hir> { parenting: LocalDefIdMap, /// 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>, num_nodes: usize, ) -> (IndexVec>, LocalDefIdMap) { - 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); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 8d4d0833eef..85d04f7d1c4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -461,7 +461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { where T: TypeVisitable>, { - 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> { diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 8e0b1b38366..0998b463a88 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -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 @@ -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 FilenameRefs; + SmallVector FilenameRefs; FilenameRefs.reserve(FilenamesLen); for (size_t i = 0; i < FilenamesLen; i++) { FilenameRefs.emplace_back(Filenames[i], Lengths[i]); } - auto FilenamesWriter = - coverage::CoverageFilenamesSectionWriter(ArrayRef(FilenameRefs)); + auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter( + ArrayRef(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 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(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(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); } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 42a06c968c7..94d1039c763 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -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), } }) }; diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 16842c8208f..054772daab8 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -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. diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 43931205017..98f01fe8772 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -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(()) diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index d4c8f5900f9..58543674460 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -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); diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index ab9fa165a20..60513a674af 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -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"); } } diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index e081a9100e2..f2c441dcbed 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -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>, 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> { - 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), - } - } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 5e580df01cb..35f7d1d7151 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -312,11 +312,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec> { 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)] diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 619435d2e8d..4a4efb6884f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -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>, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs deleted file mode 100644 index c8f9a461adf..00000000000 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs +++ /dev/null @@ -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( - &mut self, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result, - ) -> Result { - 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 - } -} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 5d9b588b55b..1739bd70e7b 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -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>>, - ) -> impl Iterator>)> { - 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. diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index cfec2e9bbf3..56c32d3d539 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -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, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index 4da999f2406..43c76cc5f4a 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -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 }) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index b1c03e82cab..5b45e1a34e4 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -177,7 +177,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, 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> for NormalizationFolder<'_, 'tcx> { fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result, 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); } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index d8aeadd07b3..fb296d55100 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -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"); } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs index 356c3776c04..9fdb280cdc6 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs @@ -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( diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 88ac9d4dbe5..a04a3bc6ebe 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -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, diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 15bfffef3ce..b4969926f64 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -101,6 +101,8 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( 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 diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 9d744d9a032..9246a41a2bc 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -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); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 3b33f6e6144..279d96dec72 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -15,7 +15,7 @@ where type QueryResponse = T; fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { - 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( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8fb8d21ac90..aa4ab9c7ee9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -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(); diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 4ecb0ea7cd9..509727cdeab 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -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))); } } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index cd199222d90..997b410f819 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -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() diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 839e75dba4c..d6a3f9f0749 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -223,8 +223,8 @@ pub trait TypeVisitableExt: TypeVisitable { 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 { diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 9b5da935e07..25a06f121cd 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -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”. diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 175c27662d0..8c5e5ecf5fe 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -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)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index f0656f997fd..2c82eda9a58 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -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() 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, { } } diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 07feb823492..14a8c245756 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.17.0 \ No newline at end of file +0.17.1 \ No newline at end of file diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index 7179ee0cf03..cb4ec51caf2 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -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 /docs/target/doc -L diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index f425f3ec95c..ccb97d7df4c 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -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; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 9993dfb1d8c..0bb073b1cea 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -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; diff --git a/src/tools/tidy/src/rustdoc_css_themes.rs b/src/tools/tidy/src/rustdoc_css_themes.rs index 852d6e14e91..af36f9ba58e 100644 --- a/src/tools/tidy/src/rustdoc_css_themes.rs +++ b/src/tools/tidy/src/rustdoc_css_themes.rs @@ -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 diff --git a/tests/rustdoc-gui/javascript-disabled.goml b/tests/rustdoc-gui/javascript-disabled.goml index a0872d553af..a7579ef7ec1 100644 --- a/tests/rustdoc-gui/javascript-disabled.goml +++ b/tests/rustdoc-gui/javascript-disabled.goml @@ -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"}) diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr index 06ffff057f9..d913b2e91ca 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr @@ -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:: | +++++++++++ -error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::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(::RefType<'a>, ::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(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::} <: ...` - --> $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`. diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr index df2ec4ab182..d913b2e91ca 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr @@ -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` diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs index 4d8ea9d8d48..acfebad38db 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs @@ -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( ) -> 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() {} diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr b/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr index 755d12d7448..c0b399746ea 100644 --- a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr +++ b/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr @@ -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::::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 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for opaque type `impl Coroutine` +LL | fn foo() -> impl Coroutine { + | --------------------------------------- + | | + | the expected opaque type + | expected `impl Coroutine` 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` + 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`. diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.rs b/tests/ui/impl-trait/recursive-coroutine-boxed.rs index 3b8ffb92090..02c75be0f3a 100644 --- a/tests/ui/impl-trait/recursive-coroutine-boxed.rs +++ b/tests/ui/impl-trait/recursive-coroutine-boxed.rs @@ -7,8 +7,10 @@ use std::ops::{Coroutine, CoroutineState}; fn foo() -> impl Coroutine { - //[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(()); diff --git a/tests/ui/sanitizer/cfi-closures.rs b/tests/ui/sanitizer/cfi-closures.rs index f3d9be35716..9f9002da674 100644 --- a/tests/ui/sanitizer/cfi-closures.rs +++ b/tests/ui/sanitizer/cfi-closures.rs @@ -77,3 +77,14 @@ fn closure_addr_taken() { let call = Fn::<()>::call; use_closure(call, &f); } + +fn use_closure_once(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); +}