提交 eca55b58 编写于 作者: N Niko Matsakis

rename to "member constraints"

上级 e39f66a8
......@@ -23,7 +23,7 @@
use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
use crate::infer::region_constraints::PickConstraint;
use crate::infer::region_constraints::MemberConstraint;
use crate::mir::interpret::ConstValue;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_macros::HashStable;
......@@ -198,14 +198,14 @@ pub struct QueryResponse<'tcx, R> {
#[derive(Clone, Debug, Default, HashStable)]
pub struct QueryRegionConstraints<'tcx> {
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
pub pick_constraints: Vec<PickConstraint<'tcx>>,
pub member_constraints: Vec<MemberConstraint<'tcx>>,
}
impl QueryRegionConstraints<'_> {
/// Represents an empty (trivially true) set of region
/// constraints.
pub fn is_empty(&self) -> bool {
self.outlives.is_empty() && self.pick_constraints.is_empty()
self.outlives.is_empty() && self.member_constraints.is_empty()
}
}
......@@ -558,14 +558,14 @@ impl<'a, 'tcx, R> Lift<'tcx> for QueryResponse<'a, R> {
BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> {
outlives, pick_constraints
outlives, member_constraints
}
}
BraceStructLiftImpl! {
impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> {
type Lifted = QueryRegionConstraints<'tcx>;
outlives, pick_constraints
outlives, member_constraints
}
}
......
......@@ -351,9 +351,9 @@ pub fn instantiate_nll_query_response_and_region_obligations<R>(
})
);
// ...also include the query pick constraints.
output_query_region_constraints.pick_constraints.extend(
query_response.value.region_constraints.pick_constraints.iter().map(|p_c| {
// ...also include the query member constraints.
output_query_region_constraints.member_constraints.extend(
query_response.value.region_constraints.member_constraints.iter().map(|p_c| {
substitute_value(self.tcx, &result_subst, p_c)
})
);
......@@ -663,7 +663,7 @@ pub fn make_query_region_constraints<'tcx>(
constraints,
verifys,
givens,
pick_constraints,
member_constraints,
} = region_constraints;
assert!(verifys.is_empty());
......@@ -694,5 +694,5 @@ pub fn make_query_region_constraints<'tcx>(
)
.collect();
QueryRegionConstraints { outlives, pick_constraints: pick_constraints.clone() }
QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() }
}
......@@ -377,12 +377,12 @@ pub fn report_region_errors(
}
}
RegionResolutionError::PickConstraintFailure {
RegionResolutionError::MemberConstraintFailure {
opaque_type_def_id,
hidden_ty,
pick_region,
member_region,
span: _,
option_regions: _,
choice_regions: _,
} => {
let hidden_ty = self.resolve_vars_if_possible(&hidden_ty);
opaque_types::unexpected_hidden_region_diagnostic(
......@@ -390,7 +390,7 @@ pub fn report_region_errors(
Some(region_scope_tree),
opaque_type_def_id,
hidden_ty,
pick_region,
member_region,
).emit();
}
}
......@@ -430,7 +430,7 @@ fn process_errors(
RegionResolutionError::GenericBoundFailure(..) => true,
RegionResolutionError::ConcreteFailure(..)
| RegionResolutionError::SubSupConflict(..)
| RegionResolutionError::PickConstraintFailure { .. } => false,
| RegionResolutionError::MemberConstraintFailure { .. } => false,
};
let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
......@@ -448,7 +448,7 @@ fn process_errors(
RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
RegionResolutionError::PickConstraintFailure { span, .. } => span,
RegionResolutionError::MemberConstraintFailure { span, .. } => span,
});
errors
}
......
......@@ -3,7 +3,7 @@
use crate::hir::def_id::DefId;
use crate::infer::region_constraints::Constraint;
use crate::infer::region_constraints::GenericKind;
use crate::infer::region_constraints::PickConstraint;
use crate::infer::region_constraints::MemberConstraint;
use crate::infer::region_constraints::RegionConstraintData;
use crate::infer::region_constraints::VarInfos;
use crate::infer::region_constraints::VerifyBound;
......@@ -84,15 +84,15 @@ pub enum RegionResolutionError<'tcx> {
Region<'tcx>,
),
/// Indicates a failure of a `PickConstraint`. These arise during
/// Indicates a failure of a `MemberConstraint`. These arise during
/// impl trait processing explicitly -- basically, the impl trait's hidden type
/// included some region that it was not supposed to.
PickConstraintFailure {
MemberConstraintFailure {
span: Span,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
pick_region: Region<'tcx>,
option_regions: Vec<Region<'tcx>>,
member_region: Region<'tcx>,
choice_regions: Vec<Region<'tcx>>,
},
}
......@@ -133,7 +133,7 @@ fn infer_variable_values(
self.expand_givens(&graph);
loop {
self.expansion(&mut var_data);
if !self.enforce_pick_constraints(&graph, &mut var_data) {
if !self.enforce_member_constraints(&graph, &mut var_data) {
break;
}
}
......@@ -197,16 +197,16 @@ fn expand_givens(&mut self, graph: &RegionGraph<'_>) {
}
}
/// Enforce all pick constraints and return true if anything
/// changed. See `enforce_pick_constraint` for more details.
fn enforce_pick_constraints(
/// Enforce all member constraints and return true if anything
/// changed. See `enforce_member_constraint` for more details.
fn enforce_member_constraints(
&self,
graph: &RegionGraph<'tcx>,
var_values: &mut LexicalRegionResolutions<'tcx>,
) -> bool {
let mut any_changed = false;
for pick_constraint in &self.data.pick_constraints {
if self.enforce_pick_constraint(graph, pick_constraint, var_values) {
for member_constraint in &self.data.member_constraints {
if self.enforce_member_constraint(graph, member_constraint, var_values) {
any_changed = true;
}
}
......@@ -230,39 +230,44 @@ fn enforce_pick_constraints(
///
/// From that list, we look for a *minimal* option `'o_min`. If we
/// find one, then we can enforce that `'r: 'o_min`.
fn enforce_pick_constraint(
fn enforce_member_constraint(
&self,
graph: &RegionGraph<'tcx>,
pick_constraint: &PickConstraint<'tcx>,
member_constraint: &MemberConstraint<'tcx>,
var_values: &mut LexicalRegionResolutions<'tcx>,
) -> bool {
debug!("enforce_pick_constraint(pick_constraint={:#?})", pick_constraint);
debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint);
// the constraint is some inference variable (`vid`) which
// must be equal to one of the options
let pick_vid = match pick_constraint.pick_region {
let member_vid = match member_constraint.member_region {
ty::ReVar(vid) => *vid,
_ => return false,
};
// The current value of `vid` is a lower bound LB -- i.e., we
// know that `LB <= vid` must be true.
let pick_lower_bound: ty::Region<'tcx> = match var_values.value(pick_vid) {
let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) {
VarValue::ErrorValue => return false,
VarValue::Value(r) => r,
};
// find all the "upper bounds" -- that is, each region `b` such that
// `r0 <= b` must hold.
let (pick_upper_bounds, _) = self.collect_concrete_regions(graph, pick_vid, OUTGOING, None);
let (member_upper_bounds, _) = self.collect_concrete_regions(
graph,
member_vid,
OUTGOING,
None,
);
// get an iterator over the *available options* -- that is,
// each constraint regions `o` where `lb <= o` and `o <= ub` for all the
// get an iterator over the *available choice* -- that is,
// each choice region `c` where `lb <= c` and `c <= ub` for all the
// upper bounds `ub`.
debug!("enforce_pick_constraint: upper_bounds={:#?}", pick_upper_bounds);
let mut options = pick_constraint.option_regions.iter().filter(|option| {
self.sub_concrete_regions(pick_lower_bound, option)
&& pick_upper_bounds
debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds);
let mut options = member_constraint.choice_regions.iter().filter(|option| {
self.sub_concrete_regions(member_lower_bound, option)
&& member_upper_bounds
.iter()
.all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region))
});
......@@ -274,23 +279,23 @@ fn enforce_pick_constraint(
Some(&r) => r,
None => return false,
};
debug!("enforce_pick_constraint: least_choice={:?}", least_choice);
debug!("enforce_member_constraint: least_choice={:?}", least_choice);
for &option in options {
debug!("enforce_pick_constraint: option={:?}", option);
debug!("enforce_member_constraint: option={:?}", option);
if !self.sub_concrete_regions(least_choice, option) {
if self.sub_concrete_regions(option, least_choice) {
debug!("enforce_pick_constraint: new least choice");
debug!("enforce_member_constraint: new least choice");
least_choice = option;
} else {
debug!("enforce_pick_constraint: no least choice");
debug!("enforce_member_constraint: no least choice");
return false;
}
}
}
debug!("enforce_pick_constraint: final least choice = {:?}", least_choice);
if least_choice != pick_lower_bound {
*var_values.value_mut(pick_vid) = VarValue::Value(least_choice);
debug!("enforce_member_constraint: final least choice = {:?}", least_choice);
if least_choice != member_lower_bound {
*var_values.value_mut(member_vid) = VarValue::Value(least_choice);
true
} else {
false
......@@ -547,20 +552,20 @@ fn collect_errors(
}
}
for pick_constraint in &self.data.pick_constraints {
let pick_region = var_data.normalize(self.tcx(), pick_constraint.pick_region);
let option_regions = pick_constraint
.option_regions
for member_constraint in &self.data.member_constraints {
let member_region = var_data.normalize(self.tcx(), member_constraint.member_region);
let choice_regions = member_constraint
.choice_regions
.iter()
.map(|&option_region| var_data.normalize(self.tcx(), option_region));
if !option_regions.clone().any(|option_region| pick_region == option_region) {
let span = self.tcx().def_span(pick_constraint.opaque_type_def_id);
errors.push(RegionResolutionError::PickConstraintFailure {
.map(|&choice_region| var_data.normalize(self.tcx(), choice_region));
if !choice_regions.clone().any(|choice_region| member_region == choice_region) {
let span = self.tcx().def_span(member_constraint.opaque_type_def_id);
errors.push(RegionResolutionError::MemberConstraintFailure {
span,
opaque_type_def_id: pick_constraint.opaque_type_def_id,
hidden_ty: pick_constraint.hidden_ty,
pick_region,
option_regions: option_regions.collect(),
opaque_type_def_id: member_constraint.opaque_type_def_id,
hidden_ty: member_constraint.hidden_ty,
member_region,
choice_regions: choice_regions.collect(),
});
}
}
......
......@@ -907,7 +907,7 @@ pub fn sub_regions(
/// Require that the region `r` be equal to one of the regions in
/// the set `regions`.
pub fn pick_constraint(
pub fn member_constraint(
&self,
opaque_type_def_id: DefId,
definition_span: Span,
......@@ -915,9 +915,9 @@ pub fn pick_constraint(
region: ty::Region<'tcx>,
in_regions: &Lrc<Vec<ty::Region<'tcx>>>,
) {
debug!("pick_constraint({:?} <: {:?})", region, in_regions);
debug!("member_constraint({:?} <: {:?})", region, in_regions);
self.borrow_region_constraints()
.pick_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions);
.member_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions);
}
pub fn subtype_predicate(
......
......@@ -393,7 +393,7 @@ pub fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
// we will create a "in bound" like `'r in
// ['a, 'b, 'c]`, where `'a..'c` are the
// regions that appear in the impl trait.
return self.generate_pick_constraint(
return self.generate_member_constraint(
concrete_ty,
abstract_type_generics,
opaque_defn,
......@@ -418,17 +418,17 @@ pub fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
/// related, we would generate a constraint `'r in ['a, 'b,
/// 'static]` for each region `'r` that appears in the hidden type
/// (i.e., it must be equal to `'a`, `'b`, or `'static`).
fn generate_pick_constraint(
fn generate_member_constraint(
&self,
concrete_ty: Ty<'tcx>,
abstract_type_generics: &ty::Generics,
opaque_defn: &OpaqueTypeDecl<'tcx>,
opaque_type_def_id: DefId,
) {
// Create the set of option regions: each region in the hidden
// Create the set of choice regions: each region in the hidden
// type can be equal to any of the region parameters of the
// opaque type definition.
let option_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new(
let choice_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new(
abstract_type_generics
.params
.iter()
......@@ -443,12 +443,12 @@ fn generate_pick_constraint(
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
op: |r| self.pick_constraint(
op: |r| self.member_constraint(
opaque_type_def_id,
opaque_defn.definition_span,
concrete_ty,
r,
&option_regions,
&choice_regions,
),
});
}
......
......@@ -81,10 +81,10 @@ pub struct RegionConstraintData<'tcx> {
/// be a region variable (or neither, as it happens).
pub constraints: BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
/// Constraints of the form `pick R0 from [R1, ..., Rn]`, meaning that
/// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that
/// `R0` must be equal to one of the regions `R1..Rn`. These occur
/// with `impl Trait` quite frequently.
pub pick_constraints: Vec<PickConstraint<'tcx>>,
pub member_constraints: Vec<MemberConstraint<'tcx>>,
/// A "verify" is something that we need to verify after inference
/// is done, but which does not directly affect inference in any
......@@ -145,40 +145,40 @@ pub fn involves_placeholders(&self) -> bool {
}
}
/// Requires that `region` must be equal to one of the regions in `option_regions`.
/// Requires that `region` must be equal to one of the regions in `choice_regions`.
/// We often denote this using the syntax:
///
/// ```
/// pick R0 from [O1..On]
/// R0 member of [O1..On]
/// ```
#[derive(Debug, Clone, HashStable)]
pub struct PickConstraint<'tcx> {
pub struct MemberConstraint<'tcx> {
/// the def-id of the opaque type causing this constraint: used for error reporting
pub opaque_type_def_id: DefId,
/// the span where the hidden type was instantiated
pub definition_span: Span,
/// the hidden type in which `pick_region` appears: used for error reporting
/// the hidden type in which `member_region` appears: used for error reporting
pub hidden_ty: Ty<'tcx>,
/// the region R0
pub pick_region: Region<'tcx>,
pub member_region: Region<'tcx>,
/// the options O1..On
pub option_regions: Lrc<Vec<Region<'tcx>>>,
pub choice_regions: Lrc<Vec<Region<'tcx>>>,
}
BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for PickConstraint<'tcx> {
opaque_type_def_id, definition_span, hidden_ty, pick_region, option_regions
impl<'tcx> TypeFoldable<'tcx> for MemberConstraint<'tcx> {
opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions
}
}
BraceStructLiftImpl! {
impl<'a, 'tcx> Lift<'tcx> for PickConstraint<'a> {
type Lifted = PickConstraint<'tcx>;
opaque_type_def_id, definition_span, hidden_ty, pick_region, option_regions
impl<'a, 'tcx> Lift<'tcx> for MemberConstraint<'a> {
type Lifted = MemberConstraint<'tcx>;
opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions
}
}
......@@ -688,26 +688,26 @@ pub fn make_eqregion(
}
}
pub fn pick_constraint(
pub fn member_constraint(
&mut self,
opaque_type_def_id: DefId,
definition_span: Span,
hidden_ty: Ty<'tcx>,
pick_region: ty::Region<'tcx>,
option_regions: &Lrc<Vec<ty::Region<'tcx>>>,
member_region: ty::Region<'tcx>,
choice_regions: &Lrc<Vec<ty::Region<'tcx>>>,
) {
debug!("pick_constraint({:?} in {:#?})", pick_region, option_regions);
debug!("member_constraint({:?} in {:#?})", member_region, choice_regions);
if option_regions.iter().any(|&r| r == pick_region) {
if choice_regions.iter().any(|&r| r == member_region) {
return;
}
self.data.pick_constraints.push(PickConstraint {
self.data.member_constraints.push(MemberConstraint {
opaque_type_def_id,
definition_span,
hidden_ty,
pick_region,
option_regions: option_regions.clone()
member_region,
choice_regions: choice_regions.clone()
});
}
......@@ -975,12 +975,12 @@ impl<'tcx> RegionConstraintData<'tcx> {
pub fn is_empty(&self) -> bool {
let RegionConstraintData {
constraints,
pick_constraints,
member_constraints,
verifys,
givens,
} = self;
constraints.is_empty() &&
pick_constraints.is_empty() &&
member_constraints.is_empty() &&
verifys.is_empty() &&
givens.is_empty()
}
......
......@@ -349,7 +349,7 @@ fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
tcx.lift(&self.0).and_then(|a| {
tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))
})
}
}
}
impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
......
......@@ -37,7 +37,7 @@
mod universal_regions;
mod constraints;
mod pick_constraints;
mod member_constraints;
use self::facts::AllFacts;
use self::region_infer::RegionInferenceContext;
......@@ -130,7 +130,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
placeholder_index_to_region: _,
mut liveness_constraints,
outlives_constraints,
pick_constraints,
member_constraints,
closure_bounds_mapping,
type_tests,
} = constraints;
......@@ -152,7 +152,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
universal_region_relations,
body,
outlives_constraints,
pick_constraints,
member_constraints,
closure_bounds_mapping,
type_tests,
liveness_constraints,
......
use crate::rustc::ty::{self, Ty};
use rustc::hir::def_id::DefId;
use rustc::infer::region_constraints::PickConstraint;
use rustc::infer::region_constraints::MemberConstraint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use std::hash::Hash;
use std::ops::Index;
use syntax_pos::Span;
/// Compactly stores a set of `pick R0 in [R1...Rn]` constraints,
/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
/// indexed by the region R0.
crate struct PickConstraintSet<'tcx, R>
crate struct MemberConstraintSet<'tcx, R>
where
R: Copy + Hash + Eq,
{
/// Stores the first "pick" constraint for a given R0. This is an
/// Stores the first "member" constraint for a given R0. This is an
/// index into the `constraints` vector below.
first_constraints: FxHashMap<R, NllPickConstraintIndex>,
first_constraints: FxHashMap<R, NllMemberConstraintIndex>,
/// Stores the data about each `pick R0 from [R1..Rn]` constraint.
/// Stores the data about each `R0 member of [R1..Rn]` constraint.
/// These are organized into a linked list, so each constraint
/// contains the index of the next constraint with the same R0.
constraints: IndexVec<NllPickConstraintIndex, NllPickConstraint<'tcx>>,
constraints: IndexVec<NllMemberConstraintIndex, NllMemberConstraint<'tcx>>,
/// Stores the `R1..Rn` regions for *all* sets. For any given
/// constraint, we keep two indices so that we can pull out a
/// slice.
option_regions: Vec<ty::RegionVid>,
choice_regions: Vec<ty::RegionVid>,
}
/// Represents a `pick R0 in [R1..Rn]` constraint
crate struct NllPickConstraint<'tcx> {
next_constraint: Option<NllPickConstraintIndex>,
/// Represents a `R0 member of [R1..Rn]` constraint
crate struct NllMemberConstraint<'tcx> {
next_constraint: Option<NllMemberConstraintIndex>,
/// The opaque type whose hidden type is being inferred. (Used in error reporting.)
crate opaque_type_def_id: DefId,
......@@ -42,67 +42,70 @@
crate hidden_ty: Ty<'tcx>,
/// The region R0.
crate pick_region_vid: ty::RegionVid,
crate member_region_vid: ty::RegionVid,
/// Index of `R1` in `option_regions` vector from `PickConstraintSet`.
/// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`.
start_index: usize,
/// Index of `Rn` in `option_regions` vector from `PickConstraintSet`.
/// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`.
end_index: usize,
}
newtype_index! {
crate struct NllPickConstraintIndex {
DEBUG_FORMAT = "PickConstraintIndex({})"
crate struct NllMemberConstraintIndex {
DEBUG_FORMAT = "MemberConstraintIndex({})"
}
}
impl Default for PickConstraintSet<'tcx, ty::RegionVid> {
impl Default for MemberConstraintSet<'tcx, ty::RegionVid> {
fn default() -> Self {
Self {
first_constraints: Default::default(),
constraints: Default::default(),
option_regions: Default::default(),
choice_regions: Default::default(),
}
}
}
impl<'tcx> PickConstraintSet<'tcx, ty::RegionVid> {
impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
crate fn push_constraint(
&mut self,
p_c: &PickConstraint<'tcx>,
m_c: &MemberConstraint<'tcx>,
mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid,
) {
debug!("push_constraint(p_c={:?})", p_c);
let pick_region_vid: ty::RegionVid = to_region_vid(p_c.pick_region);
let next_constraint = self.first_constraints.get(&pick_region_vid).cloned();
let start_index = self.option_regions.len();
let end_index = start_index + p_c.option_regions.len();
debug!("push_constraint: pick_region_vid={:?}", pick_region_vid);
let constraint_index = self.constraints.push(NllPickConstraint {
debug!("push_constraint(m_c={:?})", m_c);
let member_region_vid: ty::RegionVid = to_region_vid(m_c.member_region);
let next_constraint = self.first_constraints.get(&member_region_vid).cloned();
let start_index = self.choice_regions.len();
let end_index = start_index + m_c.choice_regions.len();
debug!("push_constraint: member_region_vid={:?}", member_region_vid);
let constraint_index = self.constraints.push(NllMemberConstraint {
next_constraint,
pick_region_vid,
opaque_type_def_id: p_c.opaque_type_def_id,
definition_span: p_c.definition_span,
hidden_ty: p_c.hidden_ty,
member_region_vid,
opaque_type_def_id: m_c.opaque_type_def_id,
definition_span: m_c.definition_span,
hidden_ty: m_c.hidden_ty,
start_index,
end_index,
});
self.first_constraints.insert(pick_region_vid, constraint_index);
self.option_regions.extend(p_c.option_regions.iter().map(|&r| to_region_vid(r)));
self.first_constraints.insert(member_region_vid, constraint_index);
self.choice_regions.extend(m_c.choice_regions.iter().map(|&r| to_region_vid(r)));
}
}
impl<'tcx, R1> PickConstraintSet<'tcx, R1>
impl<'tcx, R1> MemberConstraintSet<'tcx, R1>
where
R1: Copy + Hash + Eq,
{
/// Remap the "pick region" key using `map_fn`, producing a new
/// Remap the "member region" key using `map_fn`, producing a new
/// pick-constraint set. This is used in the NLL code to map from
/// the original `RegionVid` to an scc index. In some cases, we
/// may have multiple R1 values mapping to the same R2 key -- that
/// is ok, the two sets will be merged.
crate fn into_mapped<R2>(self, mut map_fn: impl FnMut(R1) -> R2) -> PickConstraintSet<'tcx, R2>
crate fn into_mapped<R2>(
self,
mut map_fn: impl FnMut(R1) -> R2,
) -> MemberConstraintSet<'tcx, R2>
where
R2: Copy + Hash + Eq,
{
......@@ -116,7 +119,7 @@ impl<'tcx, R1> PickConstraintSet<'tcx, R1>
// link from one list to point at the othe other (see
// `append_list`).
let PickConstraintSet { first_constraints, mut constraints, option_regions } = self;
let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self;
let mut first_constraints2 = FxHashMap::default();
first_constraints2.reserve(first_constraints.len());
......@@ -129,33 +132,33 @@ impl<'tcx, R1> PickConstraintSet<'tcx, R1>
first_constraints2.insert(r2, start1);
}
PickConstraintSet {
MemberConstraintSet {
first_constraints: first_constraints2,
constraints,
option_regions,
choice_regions,
}
}
}
impl<'tcx, R> PickConstraintSet<'tcx, R>
impl<'tcx, R> MemberConstraintSet<'tcx, R>
where
R: Copy + Hash + Eq,
{
crate fn all_indices(
&self,
) -> impl Iterator<Item = NllPickConstraintIndex> {
) -> impl Iterator<Item = NllMemberConstraintIndex> {
self.constraints.indices()
}
/// Iterate down the constraint indices associated with a given
/// peek-region. You can then use `option_regions` and other
/// peek-region. You can then use `choice_regions` and other
/// methods to access data.
crate fn indices(
&self,
pick_region_vid: R,
) -> impl Iterator<Item = NllPickConstraintIndex> + '_ {
let mut next = self.first_constraints.get(&pick_region_vid).cloned();
std::iter::from_fn(move || -> Option<NllPickConstraintIndex> {
member_region_vid: R,
) -> impl Iterator<Item = NllMemberConstraintIndex> + '_ {
let mut next = self.first_constraints.get(&member_region_vid).cloned();
std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
if let Some(current) = next {
next = self.constraints[current].next_constraint;
Some(current)
......@@ -165,25 +168,25 @@ impl<'tcx, R> PickConstraintSet<'tcx, R>
})
}
/// Returns the "option regions" for a given pick constraint. This is the R1..Rn from
/// a constraint like:
/// Returns the "choice regions" for a given member
/// constraint. This is the R1..Rn from a constraint like:
///
/// ```
/// pick R0 in [R1..Rn]
/// R0 member of [R1..Rn]
/// ```
crate fn option_regions(&self, pci: NllPickConstraintIndex) -> &[ty::RegionVid] {
let NllPickConstraint { start_index, end_index, .. } = &self.constraints[pci];
&self.option_regions[*start_index..*end_index]
crate fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] {
let NllMemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
&self.choice_regions[*start_index..*end_index]
}
}
impl<'tcx, R> Index<NllPickConstraintIndex> for PickConstraintSet<'tcx, R>
impl<'tcx, R> Index<NllMemberConstraintIndex> for MemberConstraintSet<'tcx, R>
where
R: Copy + Hash + Eq,
{
type Output = NllPickConstraint<'tcx>;
type Output = NllMemberConstraint<'tcx>;
fn index(&self, i: NllPickConstraintIndex) -> &NllPickConstraint<'tcx> {
fn index(&self, i: NllMemberConstraintIndex) -> &NllMemberConstraint<'tcx> {
&self.constraints[i]
}
}
......@@ -205,9 +208,9 @@ fn index(&self, i: NllPickConstraintIndex) -> &NllPickConstraint<'tcx> {
/// target_list: A -> B -> C -> D -> E -> F -> (None)
/// ```
fn append_list(
constraints: &mut IndexVec<NllPickConstraintIndex, NllPickConstraint<'_>>,
target_list: NllPickConstraintIndex,
source_list: NllPickConstraintIndex,
constraints: &mut IndexVec<NllMemberConstraintIndex, NllMemberConstraint<'_>>,
target_list: NllMemberConstraintIndex,
source_list: NllMemberConstraintIndex,
) {
let mut p = target_list;
loop {
......
use crate::borrow_check::nll::constraints::OutlivesConstraint;
use crate::borrow_check::nll::region_infer::AppliedPickConstraint;
use crate::borrow_check::nll::region_infer::AppliedMemberConstraint;
use crate::borrow_check::nll::region_infer::RegionInferenceContext;
use crate::borrow_check::nll::type_check::Locations;
use crate::borrow_check::nll::universal_regions::DefiningTy;
......@@ -224,13 +224,13 @@ fn find_constraint_paths_between_regions(
// But pick-constraints can also give rise to `'r: 'x`
// edges that were not part of the graph initially, so
// watch out for those.
let outgoing_edges_from_picks = self.applied_pick_constraints(r)
let outgoing_edges_from_picks = self.applied_member_constraints(r)
.iter()
.map(|&AppliedPickConstraint { best_option, pick_constraint_index, .. }| {
let p_c = &self.pick_constraints[pick_constraint_index];
.map(|&AppliedMemberConstraint { min_choice, member_constraint_index, .. }| {
let p_c = &self.member_constraints[member_constraint_index];
OutlivesConstraint {
sup: r,
sub: best_option,
sub: min_choice,
locations: Locations::All(p_c.definition_span),
category: ConstraintCategory::OpaqueType,
}
......
......@@ -3,7 +3,7 @@
use crate::borrow_check::nll::constraints::{
ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
};
use crate::borrow_check::nll::pick_constraints::{PickConstraintSet, NllPickConstraintIndex};
use crate::borrow_check::nll::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
use crate::borrow_check::nll::region_infer::values::{
PlaceholderIndices, RegionElement, ToElementIndex,
};
......@@ -72,14 +72,14 @@ pub struct RegionInferenceContext<'tcx> {
/// exists if `B: A`. Computed lazilly.
rev_constraint_graph: Option<Rc<VecGraph<ConstraintSccIndex>>>,
/// The "pick R0 from [R1..Rn]" constraints, indexed by SCC.
pick_constraints: Rc<PickConstraintSet<'tcx, ConstraintSccIndex>>,
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
/// Records the pick-constraints that we applied to each scc.
/// This is useful for error reporting. Once constraint
/// propagation is done, this vector is sorted according to
/// `pick_region_scc`.
pick_constraints_applied: Vec<AppliedPickConstraint>,
/// `member_region_scc`.
member_constraints_applied: Vec<AppliedMemberConstraint>,
/// Map closure bounds to a `Span` that should be used for error reporting.
closure_bounds_mapping:
......@@ -116,30 +116,30 @@ pub struct RegionInferenceContext<'tcx> {
universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
}
/// Each time that `apply_pick_constraint` is successful, it appends
/// one of these structs to the `pick_constraints_applied` field.
/// Each time that `apply_member_constraint` is successful, it appends
/// one of these structs to the `member_constraints_applied` field.
/// This is used in error reporting to trace out what happened.
///
/// The way that `apply_pick_constraint` works is that it effectively
/// The way that `apply_member_constraint` works is that it effectively
/// adds a new lower bound to the SCC it is analyzing: so you wind up
/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
/// minimal viable option.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
struct AppliedPickConstraint {
/// The SCC that was affected. (The "pick region".)
struct AppliedMemberConstraint {
/// The SCC that was affected. (The "member region".)
///
/// The vector if `AppliedPickConstraint` elements is kept sorted
/// The vector if `AppliedMemberConstraint` elements is kept sorted
/// by this field.
pick_region_scc: ConstraintSccIndex,
member_region_scc: ConstraintSccIndex,
/// The "best option" that `apply_pick_constraint` found -- this was
/// added as an "ad-hoc" lower-bound to `pick_region_scc`.
best_option: ty::RegionVid,
/// The "best option" that `apply_member_constraint` found -- this was
/// added as an "ad-hoc" lower-bound to `member_region_scc`.
min_choice: ty::RegionVid,
/// The "pick constraint index" -- we can find out details about
/// The "member constraint index" -- we can find out details about
/// the constraint from
/// `set.pick_constraints[pick_constraint_index]`.
pick_constraint_index: NllPickConstraintIndex,
/// `set.member_constraints[member_constraint_index]`.
member_constraint_index: NllMemberConstraintIndex,
}
struct RegionDefinition<'tcx> {
......@@ -234,7 +234,7 @@ pub(crate) fn new(
universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
_body: &Body<'tcx>,
outlives_constraints: OutlivesConstraintSet,
pick_constraints_in: PickConstraintSet<'tcx, RegionVid>,
member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
closure_bounds_mapping: FxHashMap<
Location,
FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>,
......@@ -266,7 +266,8 @@ pub(crate) fn new(
let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
let pick_constraints = Rc::new(pick_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
let member_constraints =
Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
let mut result = Self {
definitions,
......@@ -275,8 +276,8 @@ pub(crate) fn new(
constraint_graph,
constraint_sccs,
rev_constraint_graph: None,
pick_constraints,
pick_constraints_applied: Vec::new(),
member_constraints,
member_constraints_applied: Vec::new(),
closure_bounds_mapping,
scc_universes,
scc_representatives,
......@@ -447,12 +448,12 @@ pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
/// Once region solving has completed, this function will return
/// the pick-constraints that were applied to the value of a given
/// region `r`. See `AppliedPickConstraint`.
fn applied_pick_constraints(&self, r: impl ToRegionVid) -> &[AppliedPickConstraint] {
/// region `r`. See `AppliedMemberConstraint`.
fn applied_member_constraints(&self, r: impl ToRegionVid) -> &[AppliedMemberConstraint] {
let scc = self.constraint_sccs.scc(r.to_region_vid());
binary_search_util::binary_search_slice(
&self.pick_constraints_applied,
|applied| applied.pick_region_scc,
&self.member_constraints_applied,
|applied| applied.member_region_scc,
&scc,
)
}
......@@ -511,7 +512,7 @@ fn solve_inner(
errors_buffer,
);
self.check_pick_constraints(infcx, mir_def_id, errors_buffer);
self.check_member_constraints(infcx, mir_def_id, errors_buffer);
let outlives_requirements = outlives_requirements.unwrap_or(vec![]);
......@@ -548,9 +549,9 @@ fn propagate_constraints(&mut self, _body: &Body<'tcx>) {
self.propagate_constraint_sccs_if_new(scc_index, visited);
}
// Sort the applied pick constraints so we can binary search
// Sort the applied member constraints so we can binary search
// through them later.
self.pick_constraints_applied.sort_by_key(|applied| applied.pick_region_scc);
self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc);
}
/// Computes the value of the SCC `scc_a` if it has not already
......@@ -597,13 +598,13 @@ fn propagate_constraint_sccs_new(
}
}
// Now take pick constraints into account
let pick_constraints = self.pick_constraints.clone();
for p_c_i in pick_constraints.indices(scc_a) {
self.apply_pick_constraint(
// Now take member constraints into account
let member_constraints = self.member_constraints.clone();
for m_c_i in member_constraints.indices(scc_a) {
self.apply_member_constraint(
scc_a,
p_c_i,
pick_constraints.option_regions(p_c_i),
m_c_i,
member_constraints.choice_regions(m_c_i),
);
}
......@@ -614,9 +615,9 @@ fn propagate_constraint_sccs_new(
);
}
/// Invoked for each `pick R0 from [R1..Rn]` constraint.
/// Invoked for each `member R0 of [R1..Rn]` constraint.
///
/// `scc` is the SCC containing R0, and `option_regions` are the
/// `scc` is the SCC containing R0, and `choice_regions` are the
/// `R1..Rn` regions -- they are always known to be universal
/// regions (and if that's not true, we just don't attempt to
/// enforce the constraint).
......@@ -625,34 +626,34 @@ fn propagate_constraint_sccs_new(
/// is considered a *lower bound*. If possible, we will modify
/// the constraint to set it equal to one of the option regions.
/// If we make any changes, returns true, else false.
fn apply_pick_constraint(
fn apply_member_constraint(
&mut self,
scc: ConstraintSccIndex,
pick_constraint_index: NllPickConstraintIndex,
option_regions: &[ty::RegionVid],
member_constraint_index: NllMemberConstraintIndex,
choice_regions: &[ty::RegionVid],
) -> bool {
debug!("apply_pick_constraint(scc={:?}, option_regions={:#?})", scc, option_regions,);
debug!("apply_member_constraint(scc={:?}, choice_regions={:#?})", scc, choice_regions,);
if let Some(uh_oh) =
option_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r))
choice_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r))
{
// FIXME(#61773): This case can only occur with
// `impl_trait_in_bindings`, I believe, and we are just
// opting not to handle it for now. See #61773 for
// details.
bug!(
"pick constraint for `{:?}` has an option region `{:?}` \
"member constraint for `{:?}` has an option region `{:?}` \
that is not a universal region",
self.pick_constraints[pick_constraint_index].opaque_type_def_id,
self.member_constraints[member_constraint_index].opaque_type_def_id,
uh_oh,
);
}
// Create a mutable vector of the options. We'll try to winnow
// them down.
let mut option_regions: Vec<ty::RegionVid> = option_regions.to_vec();
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
// The 'pick-region' in a pick-constraint is part of the
// The 'member region' in a member constraint is part of the
// hidden type, which must be in the root universe. Therefore,
// it cannot have any placeholders in its value.
assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT);
......@@ -665,35 +666,37 @@ fn apply_pick_constraint(
// The existing value for `scc` is a lower-bound. This will
// consist of some set {P} + {LB} of points {P} and
// lower-bound free regions {LB}. As each option region O is a
// lower-bound free regions {LB}. As each choice region O is a
// free region, it will outlive the points. But we can only
// consider the option O if O: LB.
option_regions.retain(|&o_r| {
choice_regions.retain(|&o_r| {
self.scc_values
.universal_regions_outlived_by(scc)
.all(|lb| self.universal_region_relations.outlives(o_r, lb))
});
debug!("apply_pick_constraint: after lb, option_regions={:?}", option_regions);
debug!("apply_member_constraint: after lb, choice_regions={:?}", choice_regions);
// Now find all the *upper bounds* -- that is, each UB is a free
// region that must outlive pick region R0 (`UB: R0`). Therefore,
// we need only keep an option O if `UB: O` for all UB.
if option_regions.len() > 1 {
// Now find all the *upper bounds* -- that is, each UB is a
// free region that must outlive the member region R0 (`UB:
// R0`). Therefore, we need only keep an option O if `UB: O`
// for all UB.
if choice_regions.len() > 1 {
let universal_region_relations = self.universal_region_relations.clone();
for ub in self.upper_bounds(scc) {
debug!("apply_pick_constraint: ub={:?}", ub);
option_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
debug!("apply_member_constraint: ub={:?}", ub);
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
}
debug!("apply_pick_constraint: after ub, option_regions={:?}", option_regions);
debug!("apply_member_constraint: after ub, choice_regions={:?}", choice_regions);
}
// If we ruled everything out, we're done.
if option_regions.is_empty() {
if choice_regions.is_empty() {
return false;
}
// Otherwise, we need to find the minimum option, if any, and take that.
debug!("apply_pick_constraint: option_regions remaining are {:#?}", option_regions);
// Otherwise, we need to find the minimum remaining choice, if
// any, and take that.
debug!("apply_member_constraint: choice_regions remaining are {:#?}", choice_regions);
let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option<ty::RegionVid> {
let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
......@@ -707,35 +710,35 @@ fn apply_pick_constraint(
None
}
};
let mut best_option = option_regions[0];
for &other_option in &option_regions[1..] {
let mut min_choice = choice_regions[0];
for &other_option in &choice_regions[1..] {
debug!(
"apply_pick_constraint: best_option={:?} other_option={:?}",
best_option, other_option,
"apply_member_constraint: min_choice={:?} other_option={:?}",
min_choice, other_option,
);
match min(best_option, other_option) {
Some(m) => best_option = m,
match min(min_choice, other_option) {
Some(m) => min_choice = m,
None => {
debug!(
"apply_pick_constraint: {:?} and {:?} are incomparable --> no best choice",
best_option, other_option,
"apply_member_constraint: {:?} and {:?} are incomparable; no min choice",
min_choice, other_option,
);
return false;
}
}
}
let best_option_scc = self.constraint_sccs.scc(best_option);
let min_choice_scc = self.constraint_sccs.scc(min_choice);
debug!(
"apply_pick_constraint: best_choice={:?} best_option_scc={:?}",
best_option,
best_option_scc,
"apply_member_constraint: min_choice={:?} best_choice_scc={:?}",
min_choice,
min_choice_scc,
);
if self.scc_values.add_region(scc, best_option_scc) {
self.pick_constraints_applied.push(AppliedPickConstraint {
pick_region_scc: scc,
best_option,
pick_constraint_index,
if self.scc_values.add_region(scc, min_choice_scc) {
self.member_constraints_applied.push(AppliedMemberConstraint {
member_region_scc: scc,
min_choice,
member_constraint_index,
});
true
......@@ -1542,42 +1545,42 @@ fn check_bound_universal_region(
diag.emit();
}
fn check_pick_constraints(
fn check_member_constraints(
&self,
infcx: &InferCtxt<'_, 'tcx>,
mir_def_id: DefId,
errors_buffer: &mut Vec<Diagnostic>,
) {
let pick_constraints = self.pick_constraints.clone();
for p_c_i in pick_constraints.all_indices() {
debug!("check_pick_constraint(p_c_i={:?})", p_c_i);
let p_c = &pick_constraints[p_c_i];
let pick_region_vid = p_c.pick_region_vid;
let member_constraints = self.member_constraints.clone();
for m_c_i in member_constraints.all_indices() {
debug!("check_member_constraint(m_c_i={:?})", m_c_i);
let m_c = &member_constraints[m_c_i];
let member_region_vid = m_c.member_region_vid;
debug!(
"check_pick_constraint: pick_region_vid={:?} with value {}",
pick_region_vid,
self.region_value_str(pick_region_vid),
"check_member_constraint: member_region_vid={:?} with value {}",
member_region_vid,
self.region_value_str(member_region_vid),
);
let option_regions = pick_constraints.option_regions(p_c_i);
debug!("check_pick_constraint: option_regions={:?}", option_regions);
let choice_regions = member_constraints.choice_regions(m_c_i);
debug!("check_member_constraint: choice_regions={:?}", choice_regions);
// did the pick-region wind up equal to any of the option regions?
if let Some(o) = option_regions.iter().find(|&&o_r| {
self.eval_equal(o_r, p_c.pick_region_vid)
if let Some(o) = choice_regions.iter().find(|&&o_r| {
self.eval_equal(o_r, m_c.member_region_vid)
}) {
debug!("check_pick_constraint: evaluated as equal to {:?}", o);
debug!("check_member_constraint: evaluated as equal to {:?}", o);
continue;
}
// if not, report an error
let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id);
let pick_region = infcx.tcx.mk_region(ty::ReVar(pick_region_vid));
let member_region = infcx.tcx.mk_region(ty::ReVar(member_region_vid));
opaque_types::unexpected_hidden_region_diagnostic(
infcx.tcx,
Some(region_scope_tree),
p_c.opaque_type_def_id,
p_c.hidden_ty,
pick_region,
m_c.opaque_type_def_id,
m_c.hidden_ty,
member_region,
)
.buffer(errors_buffer);
}
......
......@@ -53,20 +53,23 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
debug!("convert_all(query_constraints={:#?})", query_constraints);
let QueryRegionConstraints { outlives, pick_constraints } = query_constraints;
let QueryRegionConstraints { outlives, member_constraints } = query_constraints;
// Annoying: to invoke `self.to_region_vid`, we need access to
// `self.constraints`, but we also want to be mutating
// `self.pick_constraints`. For now, just swap out the value
// `self.member_constraints`. For now, just swap out the value
// we want and replace at the end.
let mut tmp = std::mem::replace(&mut self.constraints.pick_constraints, Default::default());
for pick_constraint in pick_constraints {
let mut tmp = std::mem::replace(
&mut self.constraints.member_constraints,
Default::default(),
);
for member_constraint in member_constraints {
tmp.push_constraint(
pick_constraint,
member_constraint,
|r| self.to_region_vid(r),
);
}
self.constraints.pick_constraints = tmp;
self.constraints.member_constraints = tmp;
for query_constraint in outlives {
self.convert(query_constraint);
......
......@@ -5,7 +5,7 @@
use crate::borrow_check::borrow_set::BorrowSet;
use crate::borrow_check::location::LocationTable;
use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint};
use crate::borrow_check::nll::pick_constraints::PickConstraintSet;
use crate::borrow_check::nll::member_constraints::MemberConstraintSet;
use crate::borrow_check::nll::facts::AllFacts;
use crate::borrow_check::nll::region_infer::values::LivenessValues;
use crate::borrow_check::nll::region_infer::values::PlaceholderIndex;
......@@ -129,7 +129,7 @@ pub(crate) fn type_check<'tcx>(
placeholder_index_to_region: IndexVec::default(),
liveness_constraints: LivenessValues::new(elements.clone()),
outlives_constraints: OutlivesConstraintSet::default(),
pick_constraints: PickConstraintSet::default(),
member_constraints: MemberConstraintSet::default(),
closure_bounds_mapping: Default::default(),
type_tests: Vec::default(),
};
......@@ -889,7 +889,7 @@ struct BorrowCheckContext<'a, 'tcx> {
crate outlives_constraints: OutlivesConstraintSet,
crate pick_constraints: PickConstraintSet<'tcx, RegionVid>,
crate member_constraints: MemberConstraintSet<'tcx, RegionVid>,
crate closure_bounds_mapping:
FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>>,
......@@ -2525,7 +2525,7 @@ fn prove_closure_bounds(
// constraints only come from `-> impl Trait` and
// friends which don't appear (thus far...) in
// closures.
pick_constraints: vec![],
member_constraints: vec![],
};
let bounds_mapping = closure_constraints
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册