Skip to content

Fix repr(...) validation ICE #85976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 156 additions & 78 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use rustc_ast::{self as ast, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
use rustc_ast_pretty::pprust;
use rustc_errors::{struct_span_err, Applicability};
use rustc_errors::{struct_span_err, Applicability, Handler};
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
use rustc_macros::HashStable_Generic;
use rustc_session::parse::{feature_err, ParseSess};
Expand Down Expand Up @@ -827,6 +827,150 @@ pub enum ReprAttr {
ReprNoNiche,
}

impl ReprAttr {
fn try_from_meta_item(
diagnostic: &Handler,
item: &MetaItem,
) -> Result</* incorrect attr if None */ Option<Self>, /* invalid attr */ ()> {
use ReprAttr::*;
let name = item.name_or_empty();
let expect_no_args = || {
if !item.is_word() {
struct_span_err!(
diagnostic,
item.span,
E0589,
"invalid `repr({})` attribute: no arguments expected",
name,
)
.span_suggestion(
item.path.span.shrink_to_hi().to(item.span.shrink_to_hi()),
"remove additional values",
format!("{}", name),
Applicability::MachineApplicable,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that if the suggestion is applied, the resulting code would be malformed. Change this to the following, to make it clear for the user and correct for the machines.

Suggested change
.span_suggestion(
item.path.span.shrink_to_hi().to(item.span.shrink_to_hi()),
"remove additional values",
format!("{}", name),
Applicability::MachineApplicable,
)
.span_suggestion_verbose(
item.path.span.shrink_to_hi().to(item.span.shrink_to_hi()),
"remove additional values",
String::new(),
Applicability::MachineApplicable,
)

.emit();
}
};
let expect_one_int_arg = || {
if let MetaItemKind::NameValue(ref value) = item.kind {
let mut err = struct_span_err!(
diagnostic,
item.span,
E0693,
"incorrect `repr({})` attribute format",
name
);
match value.kind {
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
err.span_suggestion(
item.span,
"use parentheses instead",
format!("{}({})", name, int),
Applicability::MachineApplicable,
);
}
ast::LitKind::Str(s, _) => {
err.span_suggestion(
item.span,
"use parentheses instead",
format!("{}({})", name, s),
Applicability::MachineApplicable,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we see if we could make these multipart_suggestions where we add the parentheses in the right place?

_ => {}
}
err.emit();
} else if item.is_word() || matches!(item.meta_item_list(), Some([])) {
struct_span_err!(
diagnostic,
item.span,
E0693,
"invalid `repr({})` attribute: expected a value",
name
)
.span_suggestion(
item.span,
"add a value",
format!("{}(/* value */)", name),
Applicability::HasPlaceholders,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.span_suggestion(
item.span,
"add a value",
format!("{}(/* value */)", name),
Applicability::HasPlaceholders,
)
.span_suggestion_verbose(
item.span.shrink_to_hi(),
"add a value",
"(/* value */)".to_string(),
Applicability::HasPlaceholders,
)

.emit();
} else if let Some([NestedMetaItem::Literal(value)]) = item.meta_item_list() {
match parse_alignment(&value.kind) {
Ok(literal) => return Some(literal),
Err(message) => {
struct_span_err!(
diagnostic,
item.span,
E0589,
"invalid `repr({})` attribute: {}",
name,
message
)
.emit();
}
};
} else if let Some([first_meta, .., last_meta]) = item.meta_item_list() {
struct_span_err!(
diagnostic,
item.span,
E0693,
"invalid `repr({})` attribute: expected only one value",
item.name_or_empty(),
)
.span_label(first_meta.span().to(last_meta.span()), "only one argument expected")
.emit();
}
return None;
};
// Every possible repr variant must be covered here or else an ICE will occur
Ok(Some(match name {
sym::C => {
expect_no_args();
ReprC
}
sym::simd => {
expect_no_args();
ReprSimd
}
sym::transparent => {
expect_no_args();
ReprTransparent
}
sym::no_niche => {
expect_no_args();
ReprNoNiche
}
sym::align => {
if let Some(arg) = expect_one_int_arg() {
ReprAlign(arg)
} else {
return Ok(None);
}
}
sym::packed => {
if item.is_word() {
ReprPacked(1)
} else if let Some(arg) = expect_one_int_arg() {
ReprPacked(arg)
} else {
return Ok(None);
}
}
name => {
if let Some(ty) = int_type_of_word(name) {
expect_no_args();
ReprInt(ty)
} else {
// Unrecognized repr attr
return Err(());
}
}
}))
}
}

#[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum IntType {
Expand All @@ -846,98 +990,32 @@ impl IntType {
}
}

/// Parse #[repr(...)] forms.
/// Parse `#[repr(...)]` forms.
///
/// Valid repr contents: any of the primitive integral type names (see
/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
/// the same discriminant size that the corresponding C enum would or C
/// structure layout, `packed` to remove padding, and `transparent` to elegate representation
/// concerns to the only non-ZST field.
pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
use ReprAttr::*;

let mut acc = Vec::new();
let diagnostic = &sess.parse_sess.span_diagnostic;
if attr.has_name(sym::repr) {
if let Some(items) = attr.meta_item_list() {
sess.mark_attr_used(attr);
for item in items {
let mut recognised = false;
if item.is_word() {
let hint = match item.name_or_empty() {
sym::C => Some(ReprC),
sym::packed => Some(ReprPacked(1)),
sym::simd => Some(ReprSimd),
sym::transparent => Some(ReprTransparent),
sym::no_niche => Some(ReprNoNiche),
name => int_type_of_word(name).map(ReprInt),
};

if let Some(h) = hint {
recognised = true;
acc.push(h);
match item
.meta_item()
.ok_or(())
.and_then(|item| ReprAttr::try_from_meta_item(diagnostic, &item))
{
Ok(None) => {
// Recognized attribute, but failed to parse (e.g. align() or packed(1, 2, 3))
}
} else if let Some((name, value)) = item.name_value_literal() {
let mut literal_error = None;
if name == sym::align {
recognised = true;
match parse_alignment(&value.kind) {
Ok(literal) => acc.push(ReprAlign(literal)),
Err(message) => literal_error = Some(message),
};
} else if name == sym::packed {
recognised = true;
match parse_alignment(&value.kind) {
Ok(literal) => acc.push(ReprPacked(literal)),
Err(message) => literal_error = Some(message),
};
Ok(Some(attr)) => acc.push(attr),
Err(()) => {
diagnostic.delay_span_bug(item.span(), "unrecognized representation hint")
}
if let Some(literal_error) = literal_error {
struct_span_err!(
diagnostic,
item.span(),
E0589,
"invalid `repr(align)` attribute: {}",
literal_error
)
.emit();
}
} else if let Some(meta_item) = item.meta_item() {
if meta_item.has_name(sym::align) {
if let MetaItemKind::NameValue(ref value) = meta_item.kind {
recognised = true;
let mut err = struct_span_err!(
diagnostic,
item.span(),
E0693,
"incorrect `repr(align)` attribute format"
);
match value.kind {
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
err.span_suggestion(
item.span(),
"use parentheses instead",
format!("align({})", int),
Applicability::MachineApplicable,
);
}
ast::LitKind::Str(s, _) => {
err.span_suggestion(
item.span(),
"use parentheses instead",
format!("align({})", s),
Applicability::MachineApplicable,
);
}
_ => {}
}
err.emit();
}
}
}
if !recognised {
// Not a word we recognize
diagnostic.delay_span_bug(item.span(), "unrecognized representation hint");
}
}
}
Expand Down
69 changes: 69 additions & 0 deletions src/test/ui/repr/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#![feature(repr_simd)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if we can make this test also check for diagnostic applicability. It might not be possible (if there are further errors afterwards), but let's confirm.

Suggested change
#![feature(repr_simd)]
// run-rustfix
#![feature(repr_simd)]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a separate file for fixable suggestions

#![feature(no_niche)]

#[repr(transparent())] //~ ERROR invalid `repr(transparent)` attribute: no arguments expected
//~| ERROR invalid `repr(transparent)` attribute: no arguments expected
struct S0(u32);

#[repr(transparent(1, 2, 3))] //~ ERROR invalid `repr(transparent)` attribute: no arguments expected
//~| ERROR invalid `repr(transparent)` attribute: no arguments expected
struct S1(u32);

#[repr(C())] //~ ERROR invalid `repr(C)` attribute: no arguments expected
//~| ERROR invalid `repr(C)` attribute: no arguments expected
struct S2(u32);

#[repr(C(1, 2, 3))] //~ ERROR invalid `repr(C)` attribute: no arguments expected
//~| ERROR invalid `repr(C)` attribute: no arguments expected
struct S3(u32);

#[repr(simd())] //~ ERROR invalid `repr(simd)` attribute: no arguments expected
//~| ERROR invalid `repr(simd)` attribute: no arguments expected
struct S4(u32);

#[repr(simd(1, 2, 3))] //~ ERROR invalid `repr(simd)` attribute: no arguments expected
//~| ERROR invalid `repr(simd)` attribute: no arguments expected
struct S5(u32);

#[repr(no_niche())] //~ ERROR invalid `repr(no_niche)` attribute: no arguments expected
//~| ERROR invalid `repr(no_niche)` attribute: no arguments expected
struct S6(u32);

#[repr(no_niche(1, 2, 3))] //~ ERROR invalid `repr(no_niche)` attribute: no arguments expected
//~| ERROR invalid `repr(no_niche)` attribute: no arguments expected
struct S7(u32);

#[repr(align())] //~ ERROR invalid `repr(align)` attribute: expected a value
//~| ERROR invalid `repr(align)` attribute: expected a value
struct S8(u32);

#[repr(align(1, 2, 3))] //~ ERROR invalid `repr(align)` attribute: expected only one value
//~| ERROR invalid `repr(align)` attribute: expected only one value
struct S9(u32);

#[repr(packed())] //~ ERROR invalid `repr(packed)` attribute: expected a value
//~| ERROR invalid `repr(packed)` attribute: expected a value
struct S10(u32);

#[repr(packed(1, 2, 3))] //~ ERROR invalid `repr(packed)` attribute: expected only one value
//~| ERROR invalid `repr(packed)` attribute: expected only one value
struct S11(u32);

#[repr(i8())] //~ ERROR invalid `repr(i8)` attribute: no arguments expected
//~| ERROR invalid `repr(i8)` attribute: no arguments expected
enum S12 { A, B }

#[repr(i8(1, 2, 3))] //~ ERROR invalid `repr(i8)` attribute: no arguments expected
//~| ERROR invalid `repr(i8)` attribute: no arguments expected
enum S13 { A, B }

#[repr] //~ ERROR malformed `repr` attribute input
struct S14(u32);

#[repr(123)] //~ ERROR meta item in `repr` must be an identifier
struct S15(u32);

#[repr("foo")] //~ ERROR meta item in `repr` must be an identifier
struct S16(u32);

fn main() {}
Loading