Change:
self.span_err(ty.span, "`virtual` structs have been removed from the language"); } <del> self.parse_where_clause(&mut generics); </del> <ins> // There is a special case worth noting here, as reported in issue #17904. // If we are parsing a tuple struct it is the case that the where clause // should follow the field list. Like so: // // struct Foo<T>(T) where T: Copy; // // If we are parsing a normal record-style struct it is the case // that the where clause comes before the body, and after the generics. // So if we look ahead and see a brace or a where-clause we begin // parsing a record style struct. // // Otherwise if we look ahead and see a paren we parse a tuple-style // struct. let (fields, ctor_id) = if self.token.is_keyword(keywords::Where) { self.parse_where_clause(&mut generics); if self.eat(&token::Semi) { // If we see a: `struct Foo<T> where T: Copy;` style decl. (Vec::new(), Some(ast::DUMMY_NODE_ID)) } else { // If we see: `struct Foo<T> where T: Copy { ... }` (self.parse_record_struct_body(&class_name), None) } // No `where` so: `struct Foo<T>;` } else if self.eat(&token::Semi) { (Vec::new(), Some(ast::DUMMY_NODE_ID)) // Record-style struct definition } else if self.token == token::OpenDelim(token::Brace) { let fields = self.parse_record_struct_body(&class_name); (fields, None) // Tuple-style struct definition with optional where-clause. } else { let fields = self.parse_tuple_struct_body(&class_name, &mut generics); (fields, Some(ast::DUMMY_NODE_ID)) }; </ins> <del> let mut fields: Vec<StructField>; let is_tuple_like; </del> <ins> (class_name, ItemStruct(P(ast::StructDef { fields: fields, ctor_id: ctor_id, }), generics), None) } pub fn parse_record_struct_body(&mut self, class_name: &ast::Ident) -> Vec<StructField> { let mut fields = Vec::new(); </ins> if self.eat(&token::OpenDelim(token::Brace)) { <del> // It's a record-like struct. is_tuple_like = false; fields = Vec::new(); </del> while self.token != token::CloseDelim(token::Brace) { fields.push(self.parse_struct_decl_field(true)); } if fields.len() == 0 { self.fatal(format!("unit-like struct definition should be  <del> written as `struct {};`", token::get_ident(class_name))[]); </del> <ins> written as `struct {};`", token::get_ident(class_name.clone()))[]); </ins> } self.bump(); <del> } else if self.check(&token::OpenDelim(token::Paren)) { // It's a tuple-like struct. is_tuple_like = true; fields = self.parse_unspanned_seq( </del> <ins> } else { let token_str = self.this_token_to_string(); self.fatal(format!("expected `where`, or `{}` after struct  name, found `{}`", "{", token_str)[]); } fields } pub fn parse_tuple_struct_body(&mut self, class_name: &ast::Ident, generics: &mut ast::Generics) -> Vec<StructField> { // This is the case where we find `struct Foo<T>(T) where T: Copy;` if self.check(&token::OpenDelim(token::Paren)) { let fields = self.parse_unspanned_seq( </ins> &token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), seq_sep_trailing_allowed(token::Comma), |p| { <del> let attrs = p.parse_outer_attributes(); let lo = p.span.lo; let struct_field_ = ast::StructField_ { kind: UnnamedField(p.parse_visibility()), id: ast::DUMMY_NODE_ID, ty: p.parse_ty_sum(), attrs: attrs, }; spanned(lo, p.span.hi, struct_field_) }); </del> <ins> let attrs = p.parse_outer_attributes(); let lo = p.span.lo; let struct_field_ = ast::StructField_ { kind: UnnamedField(p.parse_visibility()), id: ast::DUMMY_NODE_ID, ty: p.parse_ty_sum(), attrs: attrs, }; spanned(lo, p.span.hi, struct_field_) }); </ins> if fields.len() == 0 { self.fatal(format!("unit-like struct definition should be  <del> written as `struct {};`", token::get_ident(class_name))[]); </del> <ins> written as `struct {};`", token::get_ident(class_name.clone()))[]); </ins> } <ins> self.parse_where_clause(generics); </ins> self.expect(&token::Semi); <del> } else if self.eat(&token::Semi) { // It's a unit-like struct. is_tuple_like = true; fields = Vec::new(); </del> <ins> fields // This is the case where we just see struct Foo<T> where T: Copy; } else if self.token.is_keyword(keywords::Where) { self.parse_where_clause(generics); self.expect(&token::Semi); Vec::new() // This case is where we see: `struct Foo<T>;` </ins> } else { let token_str = self.this_token_to_string(); <del> self.fatal(format!("expected `{}`, `(`, or `;` after struct  name, found `{}`", "{", token_str)[]) </del> <ins> self.fatal(format!("expected `where`, `{}`, `(`, or `;` after struct  name, found `{}`", "{", token_str)[]); </ins> } <del> let _ = ast::DUMMY_NODE_ID;  // FIXME: Workaround for crazy bug. let new_id = ast::DUMMY_NODE_ID; (class_name, ItemStruct(P(ast::StructDef { fields: fields, ctor_id: if is_tuple_like { Some(new_id) } else { None }, }), generics), None) </del> } /// Parse a structure field declaration

Discussion:
(PR on top of )
seems to work. However, the syntax is awful, and requires a comma for disambiguation.
As of today nightly, that doesn't work anymore. It seems that where clauses don't work with tuple structs anymore. Any plans to support them in the future?
is this the desired syntax? I'm assuming it was chosen in order to do some kind of parsing disambiguation. I am willing to submit a small patch for this, just want clarification on the syntax.
hmm afaik they should work, and I would expect the syntax to be
I will track this one down and follow up with a PR.
You can get the current syntax to compile if the generic type bound uses the trait sugar: Nominating, since we won't be able to move the where clause to after the tuple after 1.0 without breakage.