Add a crate-custom test harness · rust-lang/rust@d697dd4 (original) (raw)

``

1

`+

use rustc_pattern_analysis::{

`

``

2

`+

constructor::{

`

``

3

`+

Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility,

`

``

4

`+

},

`

``

5

`+

usefulness::{PlaceValidity, UsefulnessReport},

`

``

6

`+

Captures, MatchArm, PatCx, PrivateUninhabitedField,

`

``

7

`+

};

`

``

8

+

``

9

`` +

/// Sets up tracing for easier debugging. Tries to look like the rustc setup.

``

``

10

`+

pub fn init_tracing() {

`

``

11

`+

use tracing_subscriber::layer::SubscriberExt;

`

``

12

`+

use tracing_subscriber::util::SubscriberInitExt;

`

``

13

`+

use tracing_subscriber::Layer;

`

``

14

`+

let _ = tracing_tree::HierarchicalLayer::default()

`

``

15

`+

.with_writer(std::io::stderr)

`

``

16

`+

.with_indent_lines(true)

`

``

17

`+

.with_ansi(true)

`

``

18

`+

.with_targets(true)

`

``

19

`+

.with_indent_amount(2)

`

``

20

`+

.with_subscriber(

`

``

21

`+

tracing_subscriber::Registry::default()

`

``

22

`+

.with(tracing_subscriber::EnvFilter::from_default_env()),

`

``

23

`+

)

`

``

24

`+

.try_init();

`

``

25

`+

}

`

``

26

+

``

27

`+

/// A simple set of types.

`

``

28

`+

#[allow(dead_code)]

`

``

29

`+

#[derive(Debug, Copy, Clone)]

`

``

30

`+

pub enum Ty {

`

``

31

`+

/// Booleans

`

``

32

`+

Bool,

`

``

33

`+

/// 8-bit unsigned integers

`

``

34

`+

U8,

`

``

35

`+

/// Tuples.

`

``

36

`+

Tuple(&'static [Ty]),

`

``

37

`` +

/// A struct with arity fields of type ty.

``

``

38

`+

BigStruct { arity: usize, ty: &'static Ty },

`

``

39

`` +

/// A enum with arity variants of type ty.

``

``

40

`+

BigEnum { arity: usize, ty: &'static Ty },

`

``

41

`+

}

`

``

42

+

``

43

`+

/// The important logic.

`

``

44

`+

impl Ty {

`

``

45

`+

pub fn sub_tys(&self, ctor: &Constructor) -> Vec {

`

``

46

`+

use Constructor::*;

`

``

47

`+

match (ctor, *self) {

`

``

48

`+

(Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(),

`

``

49

`+

(Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(),

`

``

50

`+

(Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty],

`

``

51

`+

(Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![],

`

``

52

`+

_ => panic!("Unexpected ctor {ctor:?} for type {self:?}"),

`

``

53

`+

}

`

``

54

`+

}

`

``

55

+

``

56

`+

pub fn ctor_set(&self) -> ConstructorSet {

`

``

57

`+

match *self {

`

``

58

`+

Ty::Bool => ConstructorSet::Bool,

`

``

59

`+

Ty::U8 => ConstructorSet::Integers {

`

``

60

`+

range_1: IntRange::from_range(

`

``

61

`+

MaybeInfiniteInt::new_finite_uint(0),

`

``

62

`+

MaybeInfiniteInt::new_finite_uint(255),

`

``

63

`+

RangeEnd::Included,

`

``

64

`+

),

`

``

65

`+

range_2: None,

`

``

66

`+

},

`

``

67

`+

Ty::Tuple(..) | Ty::BigStruct { .. } => ConstructorSet::Struct { empty: false },

`

``

68

`+

Ty::BigEnum { arity, .. } => ConstructorSet::Variants {

`

``

69

`+

variants: (0..arity).map(|_| VariantVisibility::Visible).collect(),

`

``

70

`+

non_exhaustive: false,

`

``

71

`+

},

`

``

72

`+

}

`

``

73

`+

}

`

``

74

+

``

75

`+

pub fn write_variant_name(

`

``

76

`+

&self,

`

``

77

`+

f: &mut std::fmt::Formatter<'_>,

`

``

78

`+

ctor: &Constructor,

`

``

79

`+

) -> std::fmt::Result {

`

``

80

`+

match (*self, ctor) {

`

``

81

`+

(Ty::Tuple(..), _) => Ok(()),

`

``

82

`+

(Ty::BigStruct { .. }, _) => write!(f, "BigStruct"),

`

``

83

`+

(Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"),

`

``

84

`+

_ => write!(f, "{:?}::{:?}", self, ctor),

`

``

85

`+

}

`

``

86

`+

}

`

``

87

`+

}

`

``

88

+

``

89

`+

/// Compute usefulness in our simple context (and set up tracing for easier debugging).

`

``

90

`+

pub fn compute_match_usefulness<'p>(

`

``

91

`+

arms: &[MatchArm<'p, Cx>],

`

``

92

`+

ty: Ty,

`

``

93

`+

scrut_validity: PlaceValidity,

`

``

94

`+

complexity_limit: Option,

`

``

95

`+

) -> Result<UsefulnessReport<'p, Cx>, ()> {

`

``

96

`+

init_tracing();

`

``

97

`+

rustc_pattern_analysis::usefulness::compute_match_usefulness(

`

``

98

`+

&Cx,

`

``

99

`+

arms,

`

``

100

`+

ty,

`

``

101

`+

scrut_validity,

`

``

102

`+

complexity_limit,

`

``

103

`+

)

`

``

104

`+

}

`

``

105

+

``

106

`+

#[derive(Debug)]

`

``

107

`+

pub struct Cx;

`

``

108

+

``

109

`` +

/// The context for pattern analysis. Forwards anything interesting to Ty methods.

``

``

110

`+

impl PatCx for Cx {

`

``

111

`+

type Ty = Ty;

`

``

112

`+

type Error = ();

`

``

113

`+

type VariantIdx = usize;

`

``

114

`+

type StrLit = ();

`

``

115

`+

type ArmData = ();

`

``

116

`+

type PatData = ();

`

``

117

+

``

118

`+

fn is_exhaustive_patterns_feature_on(&self) -> bool {

`

``

119

`+

false

`

``

120

`+

}

`

``

121

+

``

122

`+

fn is_min_exhaustive_patterns_feature_on(&self) -> bool {

`

``

123

`+

false

`

``

124

`+

}

`

``

125

+

``

126

`+

fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize {

`

``

127

`+

ty.sub_tys(ctor).len()

`

``

128

`+

}

`

``

129

+

``

130

`+

fn ctor_sub_tys<'a>(

`

``

131

`+

&'a self,

`

``

132

`+

ctor: &'a Constructor,

`

``

133

`+

ty: &'a Self::Ty,

`

``

134

`+

) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator + Captures<'a>

`

``

135

`+

{

`

``

136

`+

ty.sub_tys(ctor).into_iter().map(|ty| (ty, PrivateUninhabitedField(false)))

`

``

137

`+

}

`

``

138

+

``

139

`+

fn ctors_for_ty(&self, ty: &Self::Ty) -> Result<ConstructorSet, Self::Error> {

`

``

140

`+

Ok(ty.ctor_set())

`

``

141

`+

}

`

``

142

+

``

143

`+

fn write_variant_name(

`

``

144

`+

f: &mut std::fmt::Formatter<'_>,

`

``

145

`+

ctor: &Constructor,

`

``

146

`+

ty: &Self::Ty,

`

``

147

`+

) -> std::fmt::Result {

`

``

148

`+

ty.write_variant_name(f, ctor)

`

``

149

`+

}

`

``

150

+

``

151

`+

fn bug(&self, fmt: std::fmt::Arguments<'_>) -> Self::Error {

`

``

152

`+

panic!("{}", fmt)

`

``

153

`+

}

`

``

154

+

``

155

`+

/// Abort when reaching the complexity limit. This is what we'll check in tests.

`

``

156

`+

fn complexity_exceeded(&self) -> Result<(), Self::Error> {

`

``

157

`+

Err(())

`

``

158

`+

}

`

``

159

`+

}

`

``

160

+

``

161

`` +

/// Construct a single pattern; see pats!().

``

``

162

`+

#[allow(unused_macros)]

`

``

163

`+

macro_rules! pat {

`

``

164

`+

($($rest:tt)*) => {{

`

``

165

`+

let mut vec = pats!($($rest)*);

`

``

166

`+

vec.pop().unwrap()

`

``

167

`+

}};

`

``

168

`+

}

`

``

169

+

``

170

`` +

/// A macro to construct patterns. Called like pats!(type_expr; pattern, pattern, ..) and returns

``

``

171

`` +

/// a Vec<DeconstructedPat>. A pattern can be nested and looks like Constructor(pat, pat) or

``

``

172

`` +

/// Constructor { .i: pat, .j: pat }, where Constructor is Struct, Variant.i (with index

``

``

173

`` +

/// i), as well as booleans and integer ranges.

``

``

174

`+

///

`

``

175

`+

/// The general structure of the macro is a tt-muncher with several stages identified with

`

``

176

`` +

/// @something(args). The args are a key-value list (the keys ensure we don't mix the arguments

``

``

177

`+

/// around) which is passed down and modified as needed. We then parse token-trees from

`

``

178

`+

/// left-to-right. Non-trivial recursion happens when we parse the arguments to a pattern: we

`

``

179

`` +

/// recurse to parse the tokens inside {..}/(..), and then we continue parsing anything that

``

``

180

`+

/// follows.

`

``

181

`+

macro_rules! pats {

`

``

182

`+

// Entrypoint

`

``

183

`` +

// Parse type; ..

``

``

184

`+

($ty:expr; (((rest:tt)*) => {{

`

``

185

`+

#[allow(unused_imports)]

`

``

186

`+

use rustc_pattern_analysis::{

`

``

187

`+

constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd},

`

``

188

`+

pat::DeconstructedPat,

`

``

189

`+

};

`

``

190

`+

let ty = $ty;

`

``

191

`` +

// The heart of the macro is designed to push IndexedPats into a Vec, so we work around

``

``

192

`+

// that.

`

``

193

`+

let sub_tys = ::std::iter::repeat(&ty);

`

``

194

`+

let mut vec = Vec::new();

`

``

195

`+

pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) (((rest)*);

`

``

196

`+

vec.into_iter().map(|ipat| ipat.pat).collect::<Vec<_>>()

`

``

197

`+

}};

`

``

198

+

``

199

`` +

// Parse constructor ..

``

``

200

+

``

201

`+

(@ctor($($args:tt)) true (((rest:tt)) => {{

`

``

202

`+

let ctor = Constructor::Bool(true);

`

``

203

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

204

`+

}};

`

``

205

`+

(@ctor($($args:tt)) false (((rest:tt)) => {{

`

``

206

`+

let ctor = Constructor::Bool(false);

`

``

207

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

208

`+

}};

`

``

209

`+

(@ctor($($args:tt)) Struct (((rest:tt)) => {{

`

``

210

`+

let ctor = Constructor::Struct;

`

``

211

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

212

`+

}};

`

``

213

`+

(@ctor($($args:tt)) ( (((fields:tt) ) (((rest:tt)*) => {{

`

``

214

`+

let ctor = Constructor::Struct; // tuples

`

``

215

`+

pats!(@pat($($args), ctor:ctor) ( (((fields) ) (((rest)*)

`

``

216

`+

}};

`

``

217

`+

(@ctor($($args:tt)) Variant.$variant:ident (((rest:tt)) => {{

`

``

218

`+

let ctor = Constructor::Variant($variant);

`

``

219

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

220

`+

}};

`

``

221

`+

(@ctor($($args:tt)) Variant.$variant:literal (((rest:tt)) => {{

`

``

222

`+

let ctor = Constructor::Variant($variant);

`

``

223

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

224

`+

}};

`

``

225

`+

(@ctor($($args:tt)) _ (((rest:tt)) => {{

`

``

226

`+

let ctor = Constructor::Wildcard;

`

``

227

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

228

`+

}};

`

``

229

+

``

230

`+

// Integers and int ranges

`

``

231

`+

(@ctor($($args:tt)) (((start:literal)?..$end:literal (((rest:tt)) => {{

`

``

232

`+

let ctor = Constructor::IntRange(IntRange::from_range(

`

``

233

`+

pats!(@rangeboundary- (((start)?),

`

``

234

`+

pats!(@rangeboundary+ $end),

`

``

235

`+

RangeEnd::Excluded,

`

``

236

`+

));

`

``

237

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

238

`+

}};

`

``

239

`+

(@ctor($($args:tt)) (((start:literal)?.. (((rest:tt)) => {{

`

``

240

`+

let ctor = Constructor::IntRange(IntRange::from_range(

`

``

241

`+

pats!(@rangeboundary- (((start)?),

`

``

242

`+

pats!(@rangeboundary+),

`

``

243

`+

RangeEnd::Excluded,

`

``

244

`+

));

`

``

245

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

246

`+

}};

`

``

247

`+

(@ctor($($args:tt)) (((start:literal)?..=$end:literal (((rest:tt)) => {{

`

``

248

`+

let ctor = Constructor::IntRange(IntRange::from_range(

`

``

249

`+

pats!(@rangeboundary- (((start)?),

`

``

250

`+

pats!(@rangeboundary+ $end),

`

``

251

`+

RangeEnd::Included,

`

``

252

`+

));

`

``

253

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

254

`+

}};

`

``

255

`+

(@ctor($($args:tt)) int:literalint:literal int:literal($rest:tt)) => {{

`

``

256

`+

let ctor = Constructor::IntRange(IntRange::from_range(

`

``

257

`+

pats!(@rangeboundary- $int),

`

``

258

`+

pats!(@rangeboundary+ $int),

`

``

259

`+

RangeEnd::Included,

`

``

260

`+

));

`

``

261

`+

pats!(@pat($($args), ctor:ctor) (((rest))

`

``

262

`+

}};

`

``

263

`+

// Utility to manage range boundaries.

`

``

264

`+

(@rangeboundary sign:ttsign:tt sign:ttint:literal) => { MaybeInfiniteInt::new_finite_uint($int) };

`

``

265

`+

(@rangeboundary -) => { MaybeInfiniteInt::NegInfinity };

`

``

266

`+

(@rangeboundary +) => { MaybeInfiniteInt::PosInfinity };

`

``

267

+

``

268

`` +

// Parse subfields: (..) or {..}

``

``

269

+

``

270

`` +

// Constructor with no fields, e.g. bool or Variant.1.

``

``

271

`+

(@pat($($args:tt)*) $(,)?) => {

`

``

272

`+

pats!(@pat($($args)*) {})

`

``

273

`+

};

`

``

274

`+

(@pat($($args:tt)) , (((rest:tt)) => {

`

``

275

`+

pats!(@pat($($args)) {}, (((rest))

`

``

276

`+

};

`

``

277

`` +

// (..) and {..} are treated the same.

``

``

278

`+

(@pat($($args:tt)) ( (((subpat:tt) ) (((rest:tt)*) => {{

`

``

279

`+

pats!(@pat($($args)) { (((subpat) } (((rest)*)

`

``

280

`+

}};

`

``

281

`+

(@pat(vec:$vec:expr, sub_tys:$sub_tys:expr, idx:$idx:expr, ctor:$ctor:expr) { (((fields:tt)* } (((rest:tt)*) => {{

`

``

282

`+

let sub_tys = $sub_tys;

`

``

283

`+

let index = $idx;

`

``

284

`` +

// Silly dance to work with both a vec and iter::repeat().

``

``

285

`+

let ty = *(&sub_tys).clone().into_iter().nth(index).unwrap();

`

``

286

`+

let ctor = $ctor;

`

``

287

`+

let ctor_sub_tys = &ty.sub_tys(&ctor);

`

``

288

`+

#[allow(unused_mut)]

`

``

289

`+

let mut fields = Vec::new();

`

``

290

`+

// Parse subpatterns (note the leading comma).

`

``

291

`+

pats!(@fields(idx:0, vec:fields, sub_tys:ctor_sub_tys) ,$($fields)*);

`

``

292

`+

let arity = ctor_sub_tys.len();

`

``

293

`+

let pat = DeconstructedPat::new(ctor, fields, arity, ty, ()).at_index(index);

`

``

294

`+

$vec.push(pat);

`

``

295

+

``

296

`+

// Continue parsing further patterns.

`

``

297

`+

pats!(@fields(idx:index+1, vec:$vec, sub_tys:sub_tys) (((rest)*);

`

``

298

`+

}};

`

``

299

+

``

300

`+

// Parse fields one by one.

`

``

301

+

``

302

`+

// No fields left.

`

``

303

`+

(@fields($($args:tt)*) $(,)?) => {};

`

``

304

`` +

// .i: pat sets the current index to i.

``

``

305

`+

(@fields(idx:$_idx:expr, (((args:tt)) , .$idx:literal : (((rest:tt)) => {{

`

``

306

`+

pats!(@ctor($($args), idx:$idx) (((rest));

`

``

307

`+

}};

`

``

308

`+

(@fields(idx:$_idx:expr, (((args:tt)) , .$idx:ident : (((rest:tt)) => {{

`

``

309

`+

pats!(@ctor($($args), idx:$idx) (((rest));

`

``

310

`+

}};

`

``

311

`+

// Field without an explicit index; we use the current index which gets incremented above.

`

``

312

`+

(@fields(idx:$idx:expr, (((args:tt)) , (((rest:tt)) => {{

`

``

313

`+

pats!(@ctor($($args), idx:$idx) (((rest));

`

``

314

`+

}};

`

``

315

`+

}

`