Skip to content

Commit

Permalink
Auto merge of #118134 - Nilstrieb:rollup-kyo1l6e, r=Nilstrieb
Browse files Browse the repository at this point in the history
Rollup of 6 pull requests

Successful merges:

 - #116085 (rustdoc-search: add support for traits and associated types)
 - #117522 (Remove `--check-cfg` checking of command line `--cfg` args)
 - #118029 (Expand Miri's BorTag GC to a Provenance GC)
 - #118035 (Fix early param lifetimes in generic_const_exprs)
 - #118083 (Remove i686-apple-darwin cross-testing)
 - #118091 (Remove now deprecated target x86_64-sun-solaris.)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Nov 21, 2023
2 parents e24e5af + fa8878b commit 0ff8610
Show file tree
Hide file tree
Showing 96 changed files with 1,988 additions and 602 deletions.
11 changes: 9 additions & 2 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::session_diagnostics::{
LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote,
};

use super::{OutlivesSuggestionBuilder, RegionName};
use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource};
use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
use crate::{
nll::ConstraintDescription,
Expand Down Expand Up @@ -763,7 +763,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let err = LifetimeOutliveErr { span: *span };
let mut diag = self.infcx.tcx.sess.create_err(err);

let fr_name = self.give_region_a_name(*fr).unwrap();
// In certain scenarios, such as the one described in issue #118021,
// we might encounter a lifetime that cannot be named.
// These situations are bound to result in errors.
// To prevent an immediate ICE, we opt to create a dummy name instead.
let fr_name = self.give_region_a_name(*fr).unwrap_or(RegionName {
name: kw::UnderscoreLifetime,
source: RegionNameSource::Static,
});
fr_name.highlight_region_name(&mut diag);
let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
outlived_fr_name.highlight_region_name(&mut diag);
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> {
FxIndexMap::contains_key(self, k)
}

#[inline(always)]
fn contains_key_ref<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> bool
where
K: Borrow<Q>,
{
FxIndexMap::contains_key(self, k)
}

#[inline(always)]
fn insert(&mut self, k: K, v: V) -> Option<V> {
FxIndexMap::insert(self, k, v)
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_const_eval/src/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ pub trait AllocMap<K: Hash + Eq, V> {
where
K: Borrow<Q>;

/// Callers should prefer [`AllocMap::contains_key`] when it is possible to call because it may
/// be more efficient. This function exists for callers that only have a shared reference
/// (which might make it slightly less efficient than `contains_key`, e.g. if
/// the data is stored inside a `RefCell`).
fn contains_key_ref<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> bool
where
K: Borrow<Q>;

/// Inserts a new entry into the map.
fn insert(&mut self, k: K, v: V) -> Option<V>;

Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok((&mut alloc.extra, machine))
}

/// Check whether an allocation is live. This is faster than calling
/// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is
/// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics.
pub fn is_alloc_live(&self, id: AllocId) -> bool {
self.tcx.try_get_global_alloc(id).is_some()
|| self.memory.alloc_map.contains_key_ref(&id)
|| self.memory.extra_fn_ptr_map.contains_key(&id)
}

/// Obtain the size and alignment of an allocation, even if that allocation has
/// been deallocated.
pub fn get_alloc_info(&self, id: AllocId) -> (Size, Align, AllocKind) {
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,6 @@ lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not en
lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases
.suggestion = the clause will not be checked when the type alias is used, and should be removed
lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name
.help = was set with `--cfg` but isn't in the `--check-cfg` expected names
lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}`
.help = was set with `--cfg` but isn't in the `--check-cfg` expected values
lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
Expand Down
25 changes: 0 additions & 25 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ use crate::{
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue,
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
Expand All @@ -60,7 +59,6 @@ use rustc_middle::ty::GenericArgKind;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
use rustc_session::config::ExpectedValues;
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
Expand Down Expand Up @@ -2889,26 +2887,3 @@ impl EarlyLintPass for SpecialModuleName {
}
}
}

pub use rustc_session::lint::builtin::UNEXPECTED_CFGS;

declare_lint_pass!(UnexpectedCfgs => [UNEXPECTED_CFGS]);

impl EarlyLintPass for UnexpectedCfgs {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
let cfg = &cx.sess().parse_sess.config;
let check_cfg = &cx.sess().parse_sess.check_config;
for &(name, value) in cfg {
match check_cfg.expecteds.get(&name) {
Some(ExpectedValues::Some(values)) if !values.contains(&value) => {
let value = value.unwrap_or(kw::Empty);
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigValue { name, value });
}
None if check_cfg.exhaustive_names => {
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { name });
}
_ => { /* expected */ }
}
}
}
}
1 change: 0 additions & 1 deletion compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ early_lint_methods!(
IncompleteInternalFeatures: IncompleteInternalFeatures,
RedundantSemicolons: RedundantSemicolons,
UnusedDocComment: UnusedDocComment,
UnexpectedCfgs: UnexpectedCfgs,
]
]
);
Expand Down
15 changes: 0 additions & 15 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,21 +553,6 @@ pub enum BuiltinSpecialModuleNameUsed {
Main,
}

#[derive(LintDiagnostic)]
#[diag(lint_builtin_unexpected_cli_config_name)]
#[help]
pub struct BuiltinUnexpectedCliConfigName {
pub name: Symbol,
}

#[derive(LintDiagnostic)]
#[diag(lint_builtin_unexpected_cli_config_value)]
#[help]
pub struct BuiltinUnexpectedCliConfigValue {
pub name: Symbol,
pub value: Symbol,
}

// deref_into_dyn_supertrait.rs
#[derive(LintDiagnostic)]
#[diag(lint_supertrait_as_deref_target)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3439,6 +3439,7 @@ declare_lint_pass! {
UNCONDITIONAL_PANIC,
UNCONDITIONAL_RECURSION,
UNDEFINED_NAKED_FUNCTION_ABI,
UNEXPECTED_CFGS,
UNFULFILLED_LINT_EXPECTATIONS,
UNINHABITED_STATIC,
UNKNOWN_CRATE_TYPES,
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_middle/src/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,13 +525,6 @@ impl<'tcx> TyCtxt<'tcx> {
self.alloc_map.lock().reserve()
}

/// Miri's provenance GC needs to see all live allocations. The interpreter manages most
/// allocations but some are managed by [`TyCtxt`] and without this method the interpreter
/// doesn't know their [`AllocId`]s are in use.
pub fn iter_allocs<F: FnMut(AllocId)>(self, func: F) {
self.alloc_map.lock().alloc_map.keys().copied().for_each(func)
}

/// Reserves a new ID *if* this allocation has not been dedup-reserved before.
/// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
/// don't want to dedup IDs for "real" memory!
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,6 @@ supported_targets! {
("armv7r-none-eabihf", armv7r_none_eabihf),

("x86_64-pc-solaris", x86_64_pc_solaris),
("x86_64-sun-solaris", x86_64_sun_solaris),
("sparcv9-sun-solaris", sparcv9_sun_solaris),

("x86_64-unknown-illumos", x86_64_unknown_illumos),
Expand Down
20 changes: 0 additions & 20 deletions compiler/rustc_target/src/spec/targets/x86_64_sun_solaris.rs

This file was deleted.

2 changes: 1 addition & 1 deletion src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ const MIR_OPT_BLESS_TARGET_MAPPING: &[(&str, &str)] = &[
("i686-unknown-linux-musl", "x86_64-unknown-linux-musl"),
("i686-pc-windows-msvc", "x86_64-pc-windows-msvc"),
("i686-pc-windows-gnu", "x86_64-pc-windows-gnu"),
("i686-apple-darwin", "x86_64-apple-darwin"),
// ARM Macs don't have a corresponding 32-bit target that they can (easily)
// build for, so there is no entry for "aarch64-apple-darwin" here.
// Likewise, i686 for macOS is no longer possible to build.
];

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
Expand Down
6 changes: 0 additions & 6 deletions src/ci/docker/host-x86_64/dist-various-2/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ ENV \
AR_x86_64_pc_solaris=x86_64-pc-solaris2.10-ar \
CC_x86_64_pc_solaris=x86_64-pc-solaris2.10-gcc \
CXX_x86_64_pc_solaris=x86_64-pc-solaris2.10-g++ \
AR_x86_64_sun_solaris=x86_64-sun-solaris2.10-ar \
CC_x86_64_sun_solaris=x86_64-sun-solaris2.10-gcc \
CXX_x86_64_sun_solaris=x86_64-sun-solaris2.10-g++ \
CC_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-gcc-9 \
CXX_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-g++-9 \
AR_x86_64_fortanix_unknown_sgx=ar \
Expand Down Expand Up @@ -84,8 +81,6 @@ COPY host-x86_64/dist-various-2/build-fuchsia-toolchain.sh /tmp/
RUN /tmp/build-fuchsia-toolchain.sh
COPY host-x86_64/dist-various-2/build-solaris-toolchain.sh /tmp/
RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 pc
# Build deprecated target 'x86_64-sun-solaris2.10' until removed
RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 sun
RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc sun
COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/
RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh
Expand Down Expand Up @@ -120,7 +115,6 @@ ENV TARGETS=$TARGETS,wasm32-wasi
ENV TARGETS=$TARGETS,wasm32-wasi-preview1-threads
ENV TARGETS=$TARGETS,sparcv9-sun-solaris
ENV TARGETS=$TARGETS,x86_64-pc-solaris
ENV TARGETS=$TARGETS,x86_64-sun-solaris
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
ENV TARGETS=$TARGETS,x86_64-fortanix-unknown-sgx
ENV TARGETS=$TARGETS,nvptx64-nvidia-cuda
Expand Down
11 changes: 10 additions & 1 deletion src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,16 @@ cat /tmp/toolstate/toolstates.json
python3 "$X_PY" test --stage 2 check-tools
python3 "$X_PY" test --stage 2 src/tools/clippy
python3 "$X_PY" test --stage 2 src/tools/rustfmt
python3 "$X_PY" test --stage 2 src/tools/miri

# Testing Miri is a bit more complicated.
# We set the GC interval to the shortest possible value (0 would be off) to increase the chance
# that bugs which only surface when the GC runs at a specific time are more likely to cause CI to fail.
# This significantly increases the runtime of our test suite, or we'd do this in PR CI too.
if [[ -z "${PR_CI_JOB:-}" ]]; then
MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 src/tools/miri
else
python3 "$X_PY" test --stage 2 src/tools/miri
fi
# We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc.
# Also cover some other targets via cross-testing, in particular all tier 1 targets.
export BOOTSTRAP_SKIP_TARGET_SANITY=1 # we don't need `cc` for these targets
Expand Down
5 changes: 2 additions & 3 deletions src/doc/rustc/src/platform-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ target | std | notes
`riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA)
`riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA)
`sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4, glibc 2.23)
`sparcv9-sun-solaris` | ✓ | SPARC Solaris 10/11, illumos
`sparcv9-sun-solaris` | ✓ | SPARC Solaris 11, illumos
`thumbv6m-none-eabi` | * | Bare ARMv6-M
`thumbv7em-none-eabi` | * | Bare ARMv7E-M
`thumbv7em-none-eabihf` | * | Bare ARMV7E-M, hardfloat
Expand All @@ -184,7 +184,7 @@ target | std | notes
`x86_64-fuchsia` | ✓ | Alias for `x86_64-unknown-fuchsia`
[`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia
[`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android
`x86_64-pc-solaris` | ✓ | 64-bit Solaris 10/11, illumos
`x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos
`x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27)
[`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat
`x86_64-unknown-redox` | ✓ | Redox OS
Expand Down Expand Up @@ -342,7 +342,6 @@ target | std | host | notes
[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS |
[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
`x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support
`x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos
[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl
`x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
`x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku
Expand Down
47 changes: 43 additions & 4 deletions src/doc/rustdoc/src/read-documentation/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ the standard library and functions that are included in the results list:
| [`stdout, [u8]`][stdoutu8] | `Stdout::write` |
| [`any -> !`][] | `panic::panic_any` |
| [`vec::intoiter<T> -> [T]`][iterasslice] | `IntoIter::as_slice` and `IntoIter::next_chunk` |
| [`iterator<T>, fnmut -> T`][iterreduce] | `Iterator::reduce` and `Iterator::find` |

[`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std
[`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std
Expand All @@ -81,6 +82,7 @@ the standard library and functions that are included in the results list:
[`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std
[stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std
[iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter<T>%20->%20[T]&filter-crate=std
[iterreduce]: ../../std/index.html?search=iterator<T>%2C%20fnmut%20->%20T&filter-crate=std

### How type-based search works

Expand All @@ -95,16 +97,47 @@ After deciding which items are type parameters and which are actual types, it
then searches by matching up the function parameters (written before the `->`)
and the return types (written after the `->`). Type matching is order-agnostic,
and allows items to be left out of the query, but items that are present in the
query must be present in the function for it to match.
query must be present in the function for it to match. The `self` parameter is
treated the same as any other parameter, and `Self` is resolved to the
underlying type's name.

Function signature searches can query generics, wrapped in angle brackets, and
traits will be normalized like types in the search engine if no type parameters
match them. For example, a function with the signature
`fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
can be matched with the following queries:

* `Iterator<u32> -> usize`
* `Iterator -> usize`
* `Iterator<Item=u32> -> usize`
* `Iterator<u32> -> usize` (you can leave out the `Item=` part)
* `Iterator -> usize` (you can leave out iterator's generic entirely)
* `T -> usize` (you can match with a generic parameter)

Each of the above queries is progressively looser, except the last one
would not match `dyn Iterator`, since that's not a type parameter.

If a bound has multiple associated types, specifying the name allows you to
pick which one gets matched. If no name is specified, then the query will
match of any of them. For example,

```rust
pub trait MyTrait {
type First;
type Second;
}

/// This function can be found using the following search queries:
///
/// MyTrait<First=u8, Second=u32> -> bool
/// MyTrait<u32, First=u8> -> bool
/// MyTrait<Second=u32> -> bool
/// MyTrait<u32, u8> -> bool
///
/// The following queries, however, will *not* match it:
///
/// MyTrait<First=u32> -> bool
/// MyTrait<u32, u32> -> bool
pub fn my_fn(x: impl MyTrait<First=u8, Second=u32>) -> bool { true }
```

Generics and function parameters are order-agnostic, but sensitive to nesting
and number of matches. For example, a function with the signature
Expand Down Expand Up @@ -134,6 +167,10 @@ Most of these limitations should be addressed in future version of Rustdoc.
with that bound, it'll match, but `option<T> -> T where T: Default`
cannot be precisely searched for (use `option<Default> -> Default`).

* Supertraits, type aliases, and Deref are all ignored. Search mostly
operates on type signatures *as written*, and not as they are
represented within the compiler.

* Type parameters match type parameters, such that `Option<A>` matches
`Option<T>`, but never match concrete types in function signatures.
A trait named as if it were a type, such as `Option<Read>`, will match
Expand Down Expand Up @@ -183,7 +220,8 @@ slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
arg = [type-filter *WS COLON *WS] (path [generics] / slice / [!])
type-sep = COMMA/WS *(COMMA/WS)
nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
generic-arg-list = *(type-sep) arg [ EQUAL arg ] *(type-sep arg [ EQUAL arg ]) *(type-sep)
generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep)
CLOSE-ANGLE-BRACKET
return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
Expand Down Expand Up @@ -230,6 +268,7 @@ DOUBLE-COLON = "::"
QUOTE = %x22
COMMA = ","
RETURN-ARROW = "->"
EQUAL = "="
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
DIGIT = %x30-39
Expand Down
3 changes: 3 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/check-cfg.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ and `cfg!(name = "value")` call. It will check that the `"value"` specified is p
list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
lint diagnostic. The default diagnostic level for this lint is `Warn`.

The command line `--cfg` arguments are currently *NOT* checked but may very well be checked in
the future.

To enable checking of values, but to provide an empty set of expected values, use these forms:

```bash
Expand Down
Loading

0 comments on commit 0ff8610

Please sign in to comment.