Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 4 additions & 3 deletions compiler/rustc_ast_lowering/src/lib.rs
Comment thread
scrabsha marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -1750,9 +1750,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
hir::TyKind::Err(guar)
}
TyKind::View(ty, _) => {
// FIXME(scrabsha): lower view types to HIR.
return self.lower_ty(ty, itctx);
TyKind::View(ty, fields) => {
let ty = self.lower_ty_alloc(ty, itctx);
let fields = self.arena.alloc_slice(fields);
hir::TyKind::View(ty, fields)
}
TyKind::Dummy => panic!("`TyKind::Dummy` should never be lowered"),
};
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3960,6 +3960,8 @@ pub enum TyKind<'hir, Unambig = ()> {
///
/// The optional ident is the variant when an enum is passed `field_of!(Enum, Variant.field)`.
FieldOf(&'hir Ty<'hir>, &'hir TyFieldPath),
/// A view of a type. `T.{ field_1, field_2 }`.
View(&'hir Ty<'hir>, &'hir [Ident]),
/// `TyKind::Infer` means the type should be inferred instead of it having been
/// specified. This can appear anywhere in a type.
///
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,12 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -
visit_opt!(visitor, visit_ident, *variant);
try_visit!(visitor.visit_ident(*field));
}
TyKind::View(ty, fields) => {
try_visit!(visitor.visit_ty_unambig(ty));
for field in fields {
try_visit!(visitor.visit_ident(*field));
}
}
}
V::Result::output()
}
Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_hir_analysis/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2000,3 +2000,33 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for UncoveredTyParam<'_> {
diag
}
}

#[derive(Diagnostic)]
#[diag("field `{$name}` is already part of the view")]
pub(crate) struct ViewedFieldIsAlreadyPartOfTheView {
#[primary_span]
pub span: Span,
pub name: Symbol,
#[label("field `{$name}` is declared as viewed here")]
pub previous_field_span: Span,
}

#[derive(Diagnostic)]
#[diag("only structs can be viewed")]
pub(crate) struct OnlyStructsCanBeViewedNonAdt<'tcx> {
#[primary_span]
#[label("type `{$ty}` cannot be viewed")]
pub span: Span,
pub ty: Ty<'tcx>,
}

#[derive(Diagnostic)]
#[diag("only structs can be viewed")]
pub(crate) struct OnlyStructsCanBeViewedAdt<'tcx> {
#[primary_span]
#[label("`{$ty}` is {$article} {$kind}, it cannot be viewed")]
pub span: Span,
pub ty: Ty<'tcx>,
pub article: &'static str,
pub kind: &'static str,
}
75 changes: 74 additions & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use rustc_trait_selection::traits::{self, FulfillmentError};
use tracing::{debug, instrument};

use crate::check::check_abi;
use crate::diagnostics::{BadReturnTypeNotation, NoFieldOnType};
use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
Expand Down Expand Up @@ -3401,6 +3401,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
*variant,
*field,
),
hir::TyKind::View(ty, fields) => {
self.lower_view(self.lower_ty(ty), fields, hir_ty.span)
}

hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar),
};

Expand Down Expand Up @@ -3812,4 +3816,73 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let adt_ty = Ty::new_adt(tcx, adt_def, args);
ty::Const::new_value(tcx, valtree, adt_ty)
}

fn lower_view(&self, inner_ty: Ty<'tcx>, fields: &[Ident], ty_span: Span) -> Ty<'tcx> {
// Step 1: check that every field is unique, and keep a list of field that we know are
// unique.
let mut viewed_fields = Vec::<Ident>::with_capacity(fields.len());

for f in fields {
let f = f.normalize_to_macros_2_0();
// PERF: this is quadratic, but ~fine since the amount of fields is very low.
if let Some(previous_field_span) =
viewed_fields.iter().find_map(|f_| (*f_ == f).then_some(f_.span))
{
self.dcx().emit_err(diagnostics::ViewedFieldIsAlreadyPartOfTheView {
name: f.name,
span: f.span,
previous_field_span,
});
continue;
}
viewed_fields.push(f);
}

// Step 2: check that the viewed type is a struct.
let variant = match inner_ty.kind() {
ty::Adt(def, _) if def.is_struct() => def.non_enum_variant(),

ty::Adt(def, _) => {
let guar = self.dcx().emit_err(diagnostics::OnlyStructsCanBeViewedAdt {
ty: inner_ty,
span: ty_span,
article: def.article(),
kind: def.descr(),
});
return Ty::new_error(self.tcx(), guar);
}

_ => {
let guar = self.dcx().emit_err(diagnostics::OnlyStructsCanBeViewedNonAdt {
ty: inner_ty,
span: ty_span,
});
return Ty::new_error(self.tcx(), guar);
}
};

// Step 3: check that every viewed field exists.
let mut viewed_indices = Vec::with_capacity(viewed_fields.len());
let mut error = None;
for field in viewed_fields {
let Some((_, field)) = variant
.fields
.iter_enumerated()
.find(|(_, f)| f.ident(self.tcx()).normalize_to_macros_2_0() == field)
Comment thread
oli-obk marked this conversation as resolved.
else {
let err =
self.dcx().emit_err(NoFieldOnType { span: field.span, field, ty: inner_ty });
error = Some(err);
continue;
};

viewed_indices.push(field);
}
if let Some(guar) = error {
return Ty::new_error(self.tcx(), guar);
}

// FIXME(scrabsha): actually lower view types.
inner_ty
}
}
11 changes: 11 additions & 0 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,17 @@ impl<'a> State<'a> {
self.print_ident(*field);
self.word(")");
}
hir::TyKind::View(ty, fields) => {
self.word("view_type!(");
self.print_type(ty);
self.word(".{");
if !fields.is_empty() {
self.space();
self.commasep(Breaks::Inconsistent, fields, |s, f| s.print_ident(*f));
self.space();
}
self.word("})");
}
}
self.end(ib)
}
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,17 @@ impl From<AdtKind> for DataTypeKind {
}
}

impl AdtKind {
pub fn article(self) -> &'static str {
match self {
AdtKind::Struct => "a",
// https://english.stackexchange.com/a/266324
AdtKind::Union => "a",
AdtKind::Enum => "an",
}
}
}

impl AdtDefData {
/// Creates a new `AdtDefData`.
pub(super) fn new(
Expand Down Expand Up @@ -455,6 +466,14 @@ impl<'tcx> AdtDef<'tcx> {
}
}

/// Returns a description of this abstract data type with the article.
pub fn article(self) -> &'static str {
match self.adt_kind() {
AdtKind::Struct | AdtKind::Union => "a",
AdtKind::Enum => "an",
}
}

/// Returns a description of a variant of this abstract data type.
#[inline]
pub fn variant_descr(self) -> &'static str {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/input_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
Infer,
Pat,
FieldOf,
View,
Err
]
);
Expand Down
4 changes: 4 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,10 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
TyKind::UnsafeBinder(unsafe_binder_ty) => {
UnsafeBinder(Box::new(clean_unsafe_binder_ty(unsafe_binder_ty, cx)))
}
TyKind::View(ty, _) => {
// FIXME(scrabsha): propagate view types to `rustdoc`.
clean_ty(ty, cx)
}
// Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
TyKind::Infer(())
| TyKind::Err(_)
Expand Down
4 changes: 4 additions & 0 deletions src/tools/clippy/clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,10 @@ impl TyCoercionStability {
| TyKind::TraitObject(..)
| TyKind::InferDelegation(..)
| TyKind::Err(_) => Self::Reborrow,
TyKind::View(ty, _) => {
// FIXME(scrabsha): what are the semantics of view types here?
Self::for_hir_ty(ty)
}
TyKind::UnsafeBinder(..) => Self::None,
};
}
Expand Down
4 changes: 4 additions & 0 deletions src/tools/clippy/clippy_utils/src/hir_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
TyKind::UnsafeBinder(binder) => {
self.hash_ty(binder.inner_ty);
},
TyKind::View(ty, _) => {
self.hash_ty(ty);
// FIXME(scrabsha): probably hash the fields as well?
},
TyKind::Err(_)
| TyKind::Infer(())
| TyKind::Never
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/view-types/auxiliary/hygiene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@ edition:2015

#![feature(view_types, view_type_macro)]

pub use std::view::view_type;

#[macro_export]
macro_rules! view_bar {
() => {
$crate::view_type!(Bar.{ async })
}
}
30 changes: 30 additions & 0 deletions tests/ui/view-types/duplicate_field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![feature(view_types, view_type_macro)]
#![allow(unused)]

use std::view::view_type;

struct Foo {
bar: usize,
}

struct Pair(usize);

fn f(
_foo: &mut view_type!(Foo.{ bar, bar }),
//~^ ERROR field `bar` is already part of the view
_pair: &mut view_type!(Pair.{ 0, 0 }),
//~^ ERROR field `0` is already part of the view
) {
}

impl Foo {
fn f(self: &mut view_type!(Self.{ bar, bar })) {}
//~^ ERROR field `bar` is already part of the view
}

impl Pair {
fn f(self: &mut view_type!(Self.{ 0, 0 })) {}
//~^ ERROR field `0` is already part of the view
}

fn main() {}
34 changes: 34 additions & 0 deletions tests/ui/view-types/duplicate_field.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
error: field `bar` is already part of the view
--> $DIR/duplicate_field.rs:13:38
|
LL | _foo: &mut view_type!(Foo.{ bar, bar }),
| --- ^^^
| |
| field `bar` is declared as viewed here

error: field `0` is already part of the view
--> $DIR/duplicate_field.rs:15:38
|
LL | _pair: &mut view_type!(Pair.{ 0, 0 }),
| - ^
| |
| field `0` is declared as viewed here

error: field `bar` is already part of the view
--> $DIR/duplicate_field.rs:21:44
|
LL | fn f(self: &mut view_type!(Self.{ bar, bar })) {}
| --- ^^^
| |
| field `bar` is declared as viewed here

error: field `0` is already part of the view
--> $DIR/duplicate_field.rs:26:42
|
LL | fn f(self: &mut view_type!(Self.{ 0, 0 })) {}
| - ^
| |
| field `0` is declared as viewed here

error: aborting due to 4 previous errors

32 changes: 32 additions & 0 deletions tests/ui/view-types/hygiene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//@ run-pass
//@ aux-build: hygiene.rs

#![feature(view_types, view_type_macro, decl_macro)]
#![allow(unused)]

extern crate hygiene;

use std::view::view_type;

pub macro what($name: ident) {
struct Foo {
bar: usize,
$name: usize,
}

impl Foo {
fn f(self: &mut view_type!(Self.{ bar, $name })) {}
}
}

what!(bar);

struct Bar {
r#async: (),
}

impl Bar {
fn f(self: hygiene::view_bar!()) {}
}

fn main() {}
7 changes: 3 additions & 4 deletions tests/ui/view-types/must-be-struct.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//@ known-bug: unknown
//@ run-pass

#![feature(view_types, view_type_macro)]
#![allow(unused)]

Expand All @@ -11,9 +8,11 @@ enum Foo {
Baz,
}

// The following types are not structs, we expect errors here.
fn f(_: view_type!(Foo.{})) {}
//~^ ERROR only structs can be viewed
fn g(_: view_type!(u8.{})) {}
//~^ ERROR only structs can be viewed
fn h(_: view_type!(char.{})) {}
//~^ ERROR only structs can be viewed

fn main() {}
Loading
Loading