Tests of -Z print-type-sizes
functionality.
Note that the tests have been updated to initialize the local variables; originally it was enough just to declare them. Back when I started this, the `layout_cache` contained entries even just for types that had been declared but not initialized. Apparently things have changed in the interim so that if I want one of those layouts to be computed, I need to actually initialize the value. (Incidentally, this shows a weakness in the strategy of just walking the `layout_cache`; the original strategy of using a MIR visitor would probably have exhibited more robustness in terms of consistent output, but it had other weaknesses so I chose not to reimplement it. At least, not yet.) ---- Also, I have updated tests to avoid target-specific alignments.
This commit is contained in:
parent
70e5ca2ab4
commit
75825fe1df
27
src/test/ui/print_type_sizes/anonymous.rs
Normal file
27
src/test/ui/print_type_sizes/anonymous.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// All of the types that occur in this function are uninteresting, in
|
||||
// that one cannot control the sizes of these types with the same sort
|
||||
// of enum-variant manipulation tricks.
|
||||
|
||||
pub fn main() {
|
||||
let _byte: u8 = 0;
|
||||
let _word: usize = 0;
|
||||
let _tuple: (u8, usize)= (0, 0);
|
||||
let _array: [u8; 128] = [0; 128];
|
||||
let _fn: fn (u8) -> u8 = id;
|
||||
let _diverging: fn (u8) -> ! = bye;
|
||||
|
||||
fn id(x: u8) -> u8 { x };
|
||||
fn bye(_: u8) -> ! { loop { } }
|
||||
}
|
73
src/test/ui/print_type_sizes/generics.rs
Normal file
73
src/test/ui/print_type_sizes/generics.rs
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates how generics are handled: types have to be
|
||||
// monomorphized, in the MIR of the original function in which they
|
||||
// occur, to have their size reported.
|
||||
|
||||
// In an ad-hoc attempt to avoid the injection of unwinding code
|
||||
// (which clutters the output of `-Z print-type-sizes` with types from
|
||||
// `unwind::libunwind`):
|
||||
//
|
||||
// * I am not using Default to build values because that seems to
|
||||
// cause the injection of unwinding code. (Instead I just make `fn new`
|
||||
// methods.)
|
||||
//
|
||||
// * Pair derive Copy to ensure that we don't inject
|
||||
// unwinding code into generic uses of Pair when T itself is also
|
||||
// Copy.
|
||||
//
|
||||
// (I suspect this reflect some naivety within the rust compiler
|
||||
// itself; it should be checking for drop glue, i.e. a destructor
|
||||
// somewhere in the monomorphized types. It should not matter whether
|
||||
// the type is Copy.)
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Pair<T> {
|
||||
_car: T,
|
||||
_cdr: T,
|
||||
}
|
||||
|
||||
impl<T> Pair<T> {
|
||||
fn new(a: T, d: T) -> Self {
|
||||
Pair {
|
||||
_car: a,
|
||||
_cdr: d,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SevenBytes([u8; 7]);
|
||||
pub struct FiftyBytes([u8; 50]);
|
||||
|
||||
pub struct ZeroSized;
|
||||
|
||||
impl SevenBytes {
|
||||
fn new() -> Self { SevenBytes([0; 7]) }
|
||||
}
|
||||
|
||||
impl FiftyBytes {
|
||||
fn new() -> Self { FiftyBytes([0; 50]) }
|
||||
}
|
||||
|
||||
pub fn f1<T:Copy>(x: T) {
|
||||
let _v: Pair<T> = Pair::new(x, x);
|
||||
let _v2: Pair<FiftyBytes> =
|
||||
Pair::new(FiftyBytes::new(), FiftyBytes::new());
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let _b: Pair<u8> = Pair::new(0, 0);
|
||||
let _s: Pair<SevenBytes> = Pair::new(SevenBytes::new(), SevenBytes::new());
|
||||
let _z: ZeroSized = ZeroSized;
|
||||
f1::<SevenBytes>(SevenBytes::new());
|
||||
}
|
14
src/test/ui/print_type_sizes/generics.stdout
Normal file
14
src/test/ui/print_type_sizes/generics.stdout
Normal file
@ -0,0 +1,14 @@
|
||||
print-type-size type: `Pair<FiftyBytes>`: 100 bytes, alignment: 1 bytes
|
||||
print-type-size field `._car`: 50 bytes
|
||||
print-type-size field `._cdr`: 50 bytes
|
||||
print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes
|
||||
print-type-size field `.0`: 50 bytes
|
||||
print-type-size type: `Pair<SevenBytes>`: 14 bytes, alignment: 1 bytes
|
||||
print-type-size field `._car`: 7 bytes
|
||||
print-type-size field `._cdr`: 7 bytes
|
||||
print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
|
||||
print-type-size field `.0`: 7 bytes
|
||||
print-type-size type: `Pair<u8>`: 2 bytes, alignment: 1 bytes
|
||||
print-type-size field `._car`: 1 bytes
|
||||
print-type-size field `._cdr`: 1 bytes
|
||||
print-type-size type: `ZeroSized`: 0 bytes, alignment: 1 bytes
|
28
src/test/ui/print_type_sizes/multiple_types.rs
Normal file
28
src/test/ui/print_type_sizes/multiple_types.rs
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates that when multiple structural types occur in
|
||||
// a function, every one of them is included in the output.
|
||||
|
||||
pub struct SevenBytes([u8; 7]);
|
||||
pub struct FiftyBytes([u8; 50]);
|
||||
|
||||
pub enum Enum {
|
||||
Small(SevenBytes),
|
||||
Large(FiftyBytes),
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let _e: Enum;
|
||||
let _f: FiftyBytes;
|
||||
let _s: SevenBytes;
|
||||
}
|
10
src/test/ui/print_type_sizes/multiple_types.stdout
Normal file
10
src/test/ui/print_type_sizes/multiple_types.stdout
Normal file
@ -0,0 +1,10 @@
|
||||
print-type-size type: `Enum`: 51 bytes, alignment: 1 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `Small`: 7 bytes
|
||||
print-type-size field `.0`: 7 bytes
|
||||
print-type-size variant `Large`: 50 bytes
|
||||
print-type-size field `.0`: 50 bytes
|
||||
print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes
|
||||
print-type-size field `.0`: 50 bytes
|
||||
print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
|
||||
print-type-size field `.0`: 7 bytes
|
25
src/test/ui/print_type_sizes/no_duplicates.rs
Normal file
25
src/test/ui/print_type_sizes/no_duplicates.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates that when the same type occurs repeatedly
|
||||
// (even if multiple functions), it is only printed once in the
|
||||
// print-type-sizes output.
|
||||
|
||||
pub struct SevenBytes([u8; 7]);
|
||||
|
||||
pub fn f1() {
|
||||
let _s: SevenBytes = SevenBytes([0; 7]);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let _s: SevenBytes = SevenBytes([0; 7]);
|
||||
}
|
2
src/test/ui/print_type_sizes/no_duplicates.stdout
Normal file
2
src/test/ui/print_type_sizes/no_duplicates.stdout
Normal file
@ -0,0 +1,2 @@
|
||||
print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
|
||||
print-type-size field `.0`: 7 bytes
|
69
src/test/ui/print_type_sizes/nullable.rs
Normal file
69
src/test/ui/print_type_sizes/nullable.rs
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates how enums with a non-null field are handled,
|
||||
// modelled after cases like `Option<&u32>` and such.
|
||||
//
|
||||
// It uses NonZero directly, rather than `&_` or `Unique<_>`, because
|
||||
// the test is not set up to deal with target-dependent pointer width.
|
||||
//
|
||||
// It avoids using u64/i64 because on some targets that is only 4-byte
|
||||
// aligned (while on most it is 8-byte aligned) and so the resulting
|
||||
// padding and overall computed sizes can be quite different.
|
||||
|
||||
#![feature(nonzero)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
extern crate core;
|
||||
use core::nonzero::{NonZero, Zeroable};
|
||||
|
||||
pub enum MyOption<T> { None, Some(T) }
|
||||
|
||||
impl<T> Default for MyOption<T> {
|
||||
fn default() -> Self { MyOption::None }
|
||||
}
|
||||
|
||||
pub enum EmbeddedDiscr {
|
||||
None,
|
||||
Record { pre: u8, val: NonZero<u32>, post: u16 },
|
||||
}
|
||||
|
||||
impl Default for EmbeddedDiscr {
|
||||
fn default() -> Self { EmbeddedDiscr::None }
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct IndirectNonZero<T: Zeroable> {
|
||||
pre: u8,
|
||||
nested: NestedNonZero<T>,
|
||||
post: u16,
|
||||
}
|
||||
|
||||
pub struct NestedNonZero<T: Zeroable> {
|
||||
pre: u8,
|
||||
val: NonZero<T>,
|
||||
post: u16,
|
||||
}
|
||||
|
||||
impl<T: Zeroable+Default> Default for NestedNonZero<T> {
|
||||
fn default() -> Self {
|
||||
unsafe {
|
||||
NestedNonZero { pre: 0, val: NonZero::new(Default::default()), post: 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let _x: MyOption<NonZero<u32>> = Default::default();
|
||||
let _y: EmbeddedDiscr = Default::default();
|
||||
let _z: MyOption<IndirectNonZero<u32>> = Default::default();
|
||||
}
|
27
src/test/ui/print_type_sizes/nullable.stdout
Normal file
27
src/test/ui/print_type_sizes/nullable.stdout
Normal file
@ -0,0 +1,27 @@
|
||||
print-type-size type: `IndirectNonZero<u32>`: 20 bytes, alignment: 4 bytes
|
||||
print-type-size field `.pre`: 1 bytes
|
||||
print-type-size padding: 3 bytes
|
||||
print-type-size field `.nested`: 12 bytes, alignment: 4 bytes
|
||||
print-type-size field `.post`: 2 bytes
|
||||
print-type-size end padding: 2 bytes
|
||||
print-type-size type: `MyOption<IndirectNonZero<u32>>`: 20 bytes, alignment: 4 bytes
|
||||
print-type-size variant `Some`: 20 bytes
|
||||
print-type-size field `.0`: 20 bytes
|
||||
print-type-size type: `EmbeddedDiscr`: 12 bytes, alignment: 4 bytes
|
||||
print-type-size variant `Record`: 10 bytes
|
||||
print-type-size field `.pre`: 1 bytes
|
||||
print-type-size padding: 3 bytes
|
||||
print-type-size field `.val`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size field `.post`: 2 bytes
|
||||
print-type-size end padding: 2 bytes
|
||||
print-type-size type: `NestedNonZero<u32>`: 12 bytes, alignment: 4 bytes
|
||||
print-type-size field `.pre`: 1 bytes
|
||||
print-type-size padding: 3 bytes
|
||||
print-type-size field `.val`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size field `.post`: 2 bytes
|
||||
print-type-size end padding: 2 bytes
|
||||
print-type-size type: `MyOption<core::nonzero::NonZero<u32>>`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size variant `Some`: 4 bytes
|
||||
print-type-size field `.0`: 4 bytes
|
||||
print-type-size type: `core::nonzero::NonZero<u32>`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size field `.0`: 4 bytes
|
49
src/test/ui/print_type_sizes/packed.rs
Normal file
49
src/test/ui/print_type_sizes/packed.rs
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates how packing is handled; it should cause
|
||||
// the elimination of padding that would normally be introduced
|
||||
// to satisfy alignment desirata.
|
||||
//
|
||||
// It avoids using u64/i64 because on some targets that is only 4-byte
|
||||
// aligned (while on most it is 8-byte aligned) and so the resulting
|
||||
// padding and overall computed sizes can be quite different.
|
||||
|
||||
#![feature(untagged_unions)]
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
struct Packed {
|
||||
a: u8,
|
||||
b: u8,
|
||||
g: i32,
|
||||
c: u8,
|
||||
h: i16,
|
||||
d: u8,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Padded {
|
||||
a: u8,
|
||||
b: u8,
|
||||
g: i32,
|
||||
c: u8,
|
||||
h: i16,
|
||||
d: u8,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let _c: Packed = Default::default();
|
||||
let _d: Padded = Default::default();
|
||||
}
|
17
src/test/ui/print_type_sizes/packed.stdout
Normal file
17
src/test/ui/print_type_sizes/packed.stdout
Normal file
@ -0,0 +1,17 @@
|
||||
print-type-size type: `Padded`: 16 bytes, alignment: 4 bytes
|
||||
print-type-size field `.a`: 1 bytes
|
||||
print-type-size field `.b`: 1 bytes
|
||||
print-type-size padding: 2 bytes
|
||||
print-type-size field `.g`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size field `.c`: 1 bytes
|
||||
print-type-size padding: 1 bytes
|
||||
print-type-size field `.h`: 2 bytes, alignment: 2 bytes
|
||||
print-type-size field `.d`: 1 bytes
|
||||
print-type-size end padding: 3 bytes
|
||||
print-type-size type: `Packed`: 10 bytes, alignment: 1 bytes
|
||||
print-type-size field `.a`: 1 bytes
|
||||
print-type-size field `.b`: 1 bytes
|
||||
print-type-size field `.g`: 4 bytes
|
||||
print-type-size field `.c`: 1 bytes
|
||||
print-type-size field `.h`: 2 bytes
|
||||
print-type-size field `.d`: 1 bytes
|
39
src/test/ui/print_type_sizes/padding.rs
Normal file
39
src/test/ui/print_type_sizes/padding.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates how padding is handled: alignment
|
||||
// requirements can lead to the introduction of padding, either before
|
||||
// fields or at the end of the structure as a whole.
|
||||
//
|
||||
// It avoids using u64/i64 because on some targets that is only 4-byte
|
||||
// aligned (while on most it is 8-byte aligned) and so the resulting
|
||||
// padding and overall computed sizes can be quite different.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct S {
|
||||
a: bool,
|
||||
b: bool,
|
||||
g: i32,
|
||||
}
|
||||
|
||||
enum E1 {
|
||||
A(i32, i8),
|
||||
B(S),
|
||||
}
|
||||
|
||||
enum E2 {
|
||||
A(i8, i32),
|
||||
B(S),
|
||||
}
|
||||
|
||||
fn main() { }
|
21
src/test/ui/print_type_sizes/padding.stdout
Normal file
21
src/test/ui/print_type_sizes/padding.stdout
Normal file
@ -0,0 +1,21 @@
|
||||
print-type-size type: `E1`: 12 bytes, alignment: 4 bytes
|
||||
print-type-size discriminant: 4 bytes
|
||||
print-type-size variant `A`: 5 bytes
|
||||
print-type-size field `.0`: 4 bytes
|
||||
print-type-size field `.1`: 1 bytes
|
||||
print-type-size variant `B`: 8 bytes
|
||||
print-type-size field `.0`: 8 bytes
|
||||
print-type-size type: `E2`: 12 bytes, alignment: 4 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `A`: 7 bytes
|
||||
print-type-size field `.0`: 1 bytes
|
||||
print-type-size padding: 2 bytes
|
||||
print-type-size field `.1`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size variant `B`: 11 bytes
|
||||
print-type-size padding: 3 bytes
|
||||
print-type-size field `.0`: 8 bytes, alignment: 4 bytes
|
||||
print-type-size type: `S`: 8 bytes, alignment: 4 bytes
|
||||
print-type-size field `.a`: 1 bytes
|
||||
print-type-size field `.b`: 1 bytes
|
||||
print-type-size padding: 2 bytes
|
||||
print-type-size field `.g`: 4 bytes, alignment: 4 bytes
|
31
src/test/ui/print_type_sizes/variants.rs
Normal file
31
src/test/ui/print_type_sizes/variants.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates two things:
|
||||
//
|
||||
// 1. Only types that appear in a monomorphized function appear in the
|
||||
// print-type-sizes output, and
|
||||
//
|
||||
// 2. For an enum, the print-type-sizes output will also include the
|
||||
// size of each variant.
|
||||
|
||||
pub struct SevenBytes([u8; 7]);
|
||||
pub struct FiftyBytes([u8; 50]);
|
||||
|
||||
pub enum Enum {
|
||||
Small(SevenBytes),
|
||||
Large(FiftyBytes),
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let _e: Enum;
|
||||
}
|
10
src/test/ui/print_type_sizes/variants.stdout
Normal file
10
src/test/ui/print_type_sizes/variants.stdout
Normal file
@ -0,0 +1,10 @@
|
||||
print-type-size type: `Enum`: 51 bytes, alignment: 1 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `Small`: 7 bytes
|
||||
print-type-size field `.0`: 7 bytes
|
||||
print-type-size variant `Large`: 50 bytes
|
||||
print-type-size field `.0`: 50 bytes
|
||||
print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes
|
||||
print-type-size field `.0`: 50 bytes
|
||||
print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
|
||||
print-type-size field `.0`: 7 bytes
|
Loading…
Reference in New Issue
Block a user