Skip to content

Commit 5ca574e

Browse files
committed
Auto merge of #143173 - matthiaskrgr:rollup-ieu5k05, r=matthiaskrgr
Rollup of 11 pull requests Successful merges: - #142021 (Doc: clarify priority of lint level sources) - #142367 (Add regression test for #137857 to ensure that we generate intra doc links for extern crate items.) - #142641 (Generate symbols.o for proc-macros too) - #142889 (Clarify doc comment on unix OpenOptions) - #143063 (explain `ImportData::imported_module`) - #143088 (Improve documentation of `TagEncoding`) - #143135 (fix typos on some doc comments) - #143138 (Port `#[link_name]` to the new attribute parsing infrastructure) - #143155 (`librustdoc` house-keeping 🧹) - #143169 (Remove unused feature gates) - #143171 (Fix the span of trait bound modifier `[const]`) r? `@ghost` `@rustbot` modify labels: rollup
2 parents dddd7ab + a262c00 commit 5ca574e

File tree

65 files changed

+580
-400
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+580
-400
lines changed

compiler/rustc_abi/src/lib.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,24 +1592,33 @@ pub enum TagEncoding<VariantIdx: Idx> {
15921592
/// (so converting the tag to the discriminant can require sign extension).
15931593
Direct,
15941594

1595-
/// Niche (values invalid for a type) encoding the discriminant:
1596-
/// Discriminant and variant index coincide.
1595+
/// Niche (values invalid for a type) encoding the discriminant.
1596+
/// Note that for this encoding, the discriminant and variant index of each variant coincide!
1597+
/// This invariant is codified as part of [`layout_sanity_check`](../rustc_ty_utils/layout/invariant/fn.layout_sanity_check.html).
1598+
///
15971599
/// The variant `untagged_variant` contains a niche at an arbitrary
1598-
/// offset (field `tag_field` of the enum), which for a variant with
1599-
/// discriminant `d` is set to
1600-
/// `(d - niche_variants.start).wrapping_add(niche_start)`
1601-
/// (this is wrapping arithmetic using the type of the niche field).
1600+
/// offset (field [`Variants::Multiple::tag_field`] of the enum).
1601+
/// For a variant with variant index `i`, such that `i != untagged_variant`,
1602+
/// the tag is set to `(i - niche_variants.start).wrapping_add(niche_start)`
1603+
/// (this is wrapping arithmetic using the type of the niche field, cf. the
1604+
/// [`tag_for_variant`](../rustc_const_eval/interpret/struct.InterpCx.html#method.tag_for_variant)
1605+
/// query implementation).
1606+
/// To recover the variant index `i` from a `tag`, the above formula has to be reversed,
1607+
/// i.e. `i = tag.wrapping_sub(niche_start) + niche_variants.start`. If `i` ends up outside
1608+
/// `niche_variants`, the tag must have encoded the `untagged_variant`.
16021609
///
1603-
/// For example, `Option<(usize, &T)>` is represented such that
1604-
/// `None` has a null pointer for the second tuple field, and
1605-
/// `Some` is the identity function (with a non-null reference).
1610+
/// For example, `Option<(usize, &T)>` is represented such that the tag for
1611+
/// `None` is the null pointer in the second tuple field, and
1612+
/// `Some` is the identity function (with a non-null reference)
1613+
/// and has no additional tag, i.e. the reference being non-null uniquely identifies this variant.
16061614
///
16071615
/// Other variants that are not `untagged_variant` and that are outside the `niche_variants`
16081616
/// range cannot be represented; they must be uninhabited.
1617+
/// Nonetheless, uninhabited variants can also fall into the range of `niche_variants`.
16091618
Niche {
16101619
untagged_variant: VariantIdx,
1611-
/// This range *may* contain `untagged_variant`; that is then just a "dead value" and
1612-
/// not used to encode anything.
1620+
/// This range *may* contain `untagged_variant` or uninhabited variants;
1621+
/// these are then just "dead values" and not used to encode anything.
16131622
niche_variants: RangeInclusive<VariantIdx>,
16141623
/// This is inbounds of the type of the niche field
16151624
/// (not sign-extended, i.e., all bits beyond the niche field size are 0).

compiler/rustc_ast/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@
1616
#![feature(box_patterns)]
1717
#![feature(if_let_guard)]
1818
#![feature(macro_metavar_expr)]
19-
#![feature(negative_impls)]
20-
#![feature(never_type)]
2119
#![feature(rustdoc_internals)]
22-
#![feature(stmt_expr_attributes)]
2320
#![recursion_limit = "256"]
2421
// tidy-alphabetical-end
2522

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ pub enum AttributeKind {
253253
/// Represents `#[inline]` and `#[rustc_force_inline]`.
254254
Inline(InlineAttr, Span),
255255

256+
/// Represents `#[link_name]`.
257+
LinkName { name: Symbol, span: Span },
258+
256259
/// Represents `#[loop_match]`.
257260
LoopMatch(Span),
258261

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl AttributeKind {
2929
Stability { .. } => Yes,
3030
Cold(..) => No,
3131
ConstContinue(..) => No,
32+
LinkName { .. } => Yes,
3233
LoopMatch(..) => No,
3334
MayDangle(..) => No,
3435
MustUse { .. } => Yes,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use rustc_attr_data_structures::AttributeKind;
2+
use rustc_attr_data_structures::AttributeKind::LinkName;
3+
use rustc_feature::{AttributeTemplate, template};
4+
use rustc_span::{Symbol, sym};
5+
6+
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
7+
use crate::context::{AcceptContext, Stage};
8+
use crate::parser::ArgParser;
9+
10+
pub(crate) struct LinkNameParser;
11+
12+
impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
13+
const PATH: &[Symbol] = &[sym::link_name];
14+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
15+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
16+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
17+
18+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
19+
let Some(nv) = args.name_value() else {
20+
cx.expected_name_value(cx.attr_span, None);
21+
return None;
22+
};
23+
let Some(name) = nv.value_as_str() else {
24+
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
25+
return None;
26+
};
27+
28+
Some(LinkName { name, span: cx.attr_span })
29+
}
30+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub(crate) mod codegen_attrs;
3131
pub(crate) mod confusables;
3232
pub(crate) mod deprecation;
3333
pub(crate) mod inline;
34+
pub(crate) mod link_attrs;
3435
pub(crate) mod lint_helpers;
3536
pub(crate) mod loop_match;
3637
pub(crate) mod must_use;

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::attributes::codegen_attrs::{
2222
use crate::attributes::confusables::ConfusablesParser;
2323
use crate::attributes::deprecation::DeprecationParser;
2424
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
25+
use crate::attributes::link_attrs::LinkNameParser;
2526
use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
2627
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
2728
use crate::attributes::must_use::MustUseParser;
@@ -121,6 +122,7 @@ attribute_parsers!(
121122
Single<DeprecationParser>,
122123
Single<ExportNameParser>,
123124
Single<InlineParser>,
125+
Single<LinkNameParser>,
124126
Single<LoopMatchParser>,
125127
Single<MayDangleParser>,
126128
Single<MustUseParser>,

compiler/rustc_codegen_ssa/src/back/linker.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,8 +1870,13 @@ pub(crate) fn linked_symbols(
18701870
crate_type: CrateType,
18711871
) -> Vec<(String, SymbolExportKind)> {
18721872
match crate_type {
1873-
CrateType::Executable | CrateType::Cdylib | CrateType::Dylib | CrateType::Sdylib => (),
1874-
CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => {
1873+
CrateType::Executable
1874+
| CrateType::ProcMacro
1875+
| CrateType::Cdylib
1876+
| CrateType::Dylib
1877+
| CrateType::Sdylib => (),
1878+
CrateType::Staticlib | CrateType::Rlib => {
1879+
// These are not linked, so no need to generate symbols.o for them.
18751880
return Vec::new();
18761881
}
18771882
}

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
123123
}
124124
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
125125
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
126+
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
126127
AttributeKind::NoMangle(attr_span) => {
127128
if tcx.opt_item_name(did.to_def_id()).is_some() {
128129
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
@@ -262,7 +263,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
262263
}
263264
}
264265
}
265-
sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
266266
sym::link_ordinal => {
267267
link_ordinal_span = Some(attr.span());
268268
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {

compiler/rustc_codegen_ssa/src/mir/operand.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -479,17 +479,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
479479
_ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)),
480480
};
481481

482-
// Layout ensures that we only get here for cases where the discriminant
482+
// `layout_sanity_check` ensures that we only get here for cases where the discriminant
483483
// value and the variant index match, since that's all `Niche` can encode.
484-
// But for emphasis and debugging, let's double-check one anyway.
485-
debug_assert_eq!(
486-
self.layout
487-
.ty
488-
.discriminant_for_variant(bx.tcx(), untagged_variant)
489-
.unwrap()
490-
.val,
491-
u128::from(untagged_variant.as_u32()),
492-
);
493484

494485
let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
495486

compiler/rustc_error_messages/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#![doc(rust_logo)]
44
#![feature(rustc_attrs)]
55
#![feature(rustdoc_internals)]
6-
#![feature(type_alias_impl_trait)]
76
// tidy-alphabetical-end
87

98
use std::borrow::Cow;

compiler/rustc_feature/src/accepted.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ declare_features! (
341341
(accepted, pattern_parentheses, "1.31.0", Some(51087)),
342342
/// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.
343343
(accepted, precise_capturing, "1.82.0", Some(123432)),
344-
/// Allows `use<..>` precise capturign on impl Trait in traits.
344+
/// Allows `use<..>` precise capturing on impl Trait in traits.
345345
(accepted, precise_capturing_in_traits, "1.87.0", Some(130044)),
346346
/// Allows procedural macros in `proc-macro` crates.
347347
(accepted, proc_macro, "1.29.0", Some(38356)),
@@ -388,7 +388,7 @@ declare_features! (
388388
(accepted, self_struct_ctor, "1.32.0", Some(51994)),
389389
/// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics
390390
(accepted, sha512_sm_x86, "CURRENT_RUSTC_VERSION", Some(126624)),
391-
/// Shortern the tail expression lifetime
391+
/// Shorten the tail expression lifetime
392392
(accepted, shorter_tail_lifetimes, "1.84.0", Some(123739)),
393393
/// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`.
394394
(accepted, slice_patterns, "1.42.0", Some(62254)),

compiler/rustc_hir_analysis/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ This API is completely unstable and subject to change.
6565
#![feature(debug_closure_helpers)]
6666
#![feature(gen_blocks)]
6767
#![feature(if_let_guard)]
68-
#![feature(iter_from_coroutine)]
6968
#![feature(iter_intersperse)]
7069
#![feature(never_type)]
7170
#![feature(rustdoc_internals)]

compiler/rustc_lint/src/foreign_modules.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use rustc_abi::FIRST_VARIANT;
2+
use rustc_attr_data_structures::{AttributeKind, find_attr};
23
use rustc_data_structures::stack::ensure_sufficient_stack;
34
use rustc_data_structures::unord::{UnordMap, UnordSet};
45
use rustc_hir as hir;
56
use rustc_hir::def::DefKind;
67
use rustc_middle::query::Providers;
78
use rustc_middle::ty::{self, AdtDef, Instance, Ty, TyCtxt};
89
use rustc_session::declare_lint;
9-
use rustc_span::{Span, Symbol, sym};
10+
use rustc_span::{Span, Symbol};
1011
use tracing::{debug, instrument};
1112

1213
use crate::lints::{BuiltinClashingExtern, BuiltinClashingExternSub};
@@ -182,7 +183,11 @@ fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
182183
// information, we could have codegen_fn_attrs also give span information back for
183184
// where the attribute was defined. However, until this is found to be a
184185
// bottleneck, this does just fine.
185-
(overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span())
186+
(
187+
overridden_link_name,
188+
find_attr!(tcx.get_all_attrs(fi), AttributeKind::LinkName {span, ..} => *span)
189+
.unwrap(),
190+
)
186191
})
187192
{
188193
SymbolName::Link(overridden_link_name, overridden_link_name_span)

compiler/rustc_metadata/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#![feature(file_buffered)]
88
#![feature(gen_blocks)]
99
#![feature(if_let_guard)]
10-
#![feature(iter_from_coroutine)]
1110
#![feature(macro_metavar_expr)]
1211
#![feature(min_specialization)]
1312
#![feature(never_type)]

compiler/rustc_middle/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
#![feature(gen_blocks)]
4949
#![feature(if_let_guard)]
5050
#![feature(intra_doc_pointers)]
51-
#![feature(iter_from_coroutine)]
5251
#![feature(min_specialization)]
5352
#![feature(negative_impls)]
5453
#![feature(never_type)]

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,15 @@ pub enum FakeBorrowKind {
284284
///
285285
/// This is used when lowering deref patterns, where shallow borrows wouldn't prevent something
286286
/// like:
287-
// ```compile_fail
288-
// let mut b = Box::new(false);
289-
// match b {
290-
// deref!(true) => {} // not reached because `*b == false`
291-
// _ if { *b = true; false } => {} // not reached because the guard is `false`
292-
// deref!(false) => {} // not reached because the guard changed it
293-
// // UB because we reached the unreachable.
294-
// }
295-
// ```
287+
/// ```compile_fail
288+
/// let mut b = Box::new(false);
289+
/// match b {
290+
/// deref!(true) => {} // not reached because `*b == false`
291+
/// _ if { *b = true; false } => {} // not reached because the guard is `false`
292+
/// deref!(false) => {} // not reached because the guard changed it
293+
/// // UB because we reached the unreachable.
294+
/// }
295+
/// ```
296296
Deep,
297297
}
298298

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ impl<'a> Parser<'a> {
10711071
&& self.look_ahead(1, |t| t.is_keyword(kw::Const))
10721072
&& self.look_ahead(2, |t| *t == token::CloseBracket)
10731073
{
1074-
let start = self.prev_token.span;
1074+
let start = self.token.span;
10751075
self.bump();
10761076
self.expect_keyword(exp!(Const)).unwrap();
10771077
self.bump();

compiler/rustc_parse/src/validate_attr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ fn emit_malformed_attribute(
302302
| sym::no_mangle
303303
| sym::must_use
304304
| sym::track_caller
305+
| sym::link_name
305306
) {
306307
return;
307308
}

compiler/rustc_passes/src/check_attr.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
191191
Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => {
192192
self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target)
193193
}
194+
Attribute::Parsed(AttributeKind::LinkName { span: attr_span, name }) => {
195+
self.check_link_name(hir_id, *attr_span, *name, span, target)
196+
}
194197
Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
195198
self.check_may_dangle(hir_id, *attr_span)
196199
}
@@ -283,7 +286,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
283286
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
284287
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
285288
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
286-
[sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
287289
[sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
288290
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
289291
self.check_macro_use(hir_id, attr, target)
@@ -1604,35 +1606,33 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
16041606
}
16051607

16061608
/// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
1607-
fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1609+
fn check_link_name(
1610+
&self,
1611+
hir_id: HirId,
1612+
attr_span: Span,
1613+
name: Symbol,
1614+
span: Span,
1615+
target: Target,
1616+
) {
16081617
match target {
16091618
Target::ForeignFn | Target::ForeignStatic => {}
16101619
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
16111620
// `#[link_name]` attribute with just a lint, because we previously
16121621
// erroneously allowed it and some crates used it accidentally, to be compatible
16131622
// with crates depending on them, we can't throw an error here.
16141623
Target::Field | Target::Arm | Target::MacroDef => {
1615-
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_name");
1624+
self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "link_name");
16161625
}
16171626
_ => {
1618-
// FIXME: #[cold] was previously allowed on non-functions/statics and some crates
1627+
// FIXME: #[link_name] was previously allowed on non-functions/statics and some crates
16191628
// used this, so only emit a warning.
1620-
let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span());
1621-
if let Some(s) = attr.value_str() {
1622-
self.tcx.emit_node_span_lint(
1623-
UNUSED_ATTRIBUTES,
1624-
hir_id,
1625-
attr.span(),
1626-
errors::LinkName { span, attr_span, value: s.as_str() },
1627-
);
1628-
} else {
1629-
self.tcx.emit_node_span_lint(
1630-
UNUSED_ATTRIBUTES,
1631-
hir_id,
1632-
attr.span(),
1633-
errors::LinkName { span, attr_span, value: "..." },
1634-
);
1635-
};
1629+
let help_span = matches!(target, Target::ForeignMod).then_some(attr_span);
1630+
self.tcx.emit_node_span_lint(
1631+
UNUSED_ATTRIBUTES,
1632+
hir_id,
1633+
attr_span,
1634+
errors::LinkName { span, help_span, value: name.as_str() },
1635+
);
16361636
}
16371637
}
16381638
}

compiler/rustc_passes/src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ pub(crate) struct Link {
502502
#[warning]
503503
pub(crate) struct LinkName<'a> {
504504
#[help]
505-
pub attr_span: Option<Span>,
505+
pub help_span: Option<Span>,
506506
#[label]
507507
pub span: Span,
508508
pub value: &'a str,

compiler/rustc_resolve/src/imports.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,14 @@ pub(crate) struct ImportData<'ra> {
174174

175175
pub parent_scope: ParentScope<'ra>,
176176
pub module_path: Vec<Segment>,
177-
/// The resolution of `module_path`.
177+
/// The resolution of `module_path`:
178+
///
179+
/// | `module_path` | `imported_module` | remark |
180+
/// |-|-|-|
181+
/// |`use prefix::foo`| `ModuleOrUniformRoot::Module(prefix)` | - |
182+
/// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions |
183+
/// |`use ::foo` | `ModuleOrUniformRoot::CrateRootAndExternPrelude` | a special case in 2015 edition |
184+
/// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - |
178185
pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>,
179186
pub vis: ty::Visibility,
180187
}

0 commit comments

Comments
 (0)