1
+ use rustc_ast:: ast:: { FloatTy , IntTy , UintTy } ;
1
2
use rustc_errors:: struct_span_err;
2
3
use rustc_hir as hir;
3
4
use rustc_hir:: def:: { DefKind , Res } ;
@@ -7,8 +8,10 @@ use rustc_index::vec::Idx;
7
8
use rustc_middle:: ty:: layout:: { LayoutError , SizeSkeleton } ;
8
9
use rustc_middle:: ty:: query:: Providers ;
9
10
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
10
- use rustc_span:: { sym, Span } ;
11
+ use rustc_session:: lint;
12
+ use rustc_span:: { sym, Span , Symbol , DUMMY_SP } ;
11
13
use rustc_target:: abi:: { Pointer , VariantIdx } ;
14
+ use rustc_target:: asm:: { InlineAsmRegOrRegClass , InlineAsmTemplatePiece , InlineAsmType } ;
12
15
use rustc_target:: spec:: abi:: Abi :: RustIntrinsic ;
13
16
14
17
fn check_mod_intrinsics ( tcx : TyCtxt < ' _ > , module_def_id : DefId ) {
@@ -119,6 +122,262 @@ impl ExprVisitor<'tcx> {
119
122
}
120
123
err. emit ( )
121
124
}
125
+
126
+ fn is_thin_ptr_ty ( & self , ty : Ty < ' tcx > ) -> bool {
127
+ if ty. is_sized ( self . tcx . at ( DUMMY_SP ) , self . param_env ) {
128
+ return true ;
129
+ }
130
+ if let ty:: Foreign ( ..) = ty. kind {
131
+ return true ;
132
+ }
133
+ false
134
+ }
135
+
136
+ fn check_asm_operand_type (
137
+ & self ,
138
+ idx : usize ,
139
+ reg : InlineAsmRegOrRegClass ,
140
+ expr : & hir:: Expr < ' tcx > ,
141
+ template : & [ InlineAsmTemplatePiece ] ,
142
+ tied_input : Option < ( & hir:: Expr < ' tcx > , Option < InlineAsmType > ) > ,
143
+ ) -> Option < InlineAsmType > {
144
+ // Check the type against the allowed types for inline asm.
145
+ let ty = self . tables . expr_ty_adjusted ( expr) ;
146
+ let asm_ty_isize = match self . tcx . sess . target . ptr_width {
147
+ 16 => InlineAsmType :: I16 ,
148
+ 32 => InlineAsmType :: I32 ,
149
+ 64 => InlineAsmType :: I64 ,
150
+ _ => unreachable ! ( ) ,
151
+ } ;
152
+ let asm_ty = match ty. kind {
153
+ ty:: Never | ty:: Error => return None ,
154
+ ty:: Int ( IntTy :: I8 ) | ty:: Uint ( UintTy :: U8 ) => Some ( InlineAsmType :: I8 ) ,
155
+ ty:: Int ( IntTy :: I16 ) | ty:: Uint ( UintTy :: U16 ) => Some ( InlineAsmType :: I16 ) ,
156
+ ty:: Int ( IntTy :: I32 ) | ty:: Uint ( UintTy :: U32 ) => Some ( InlineAsmType :: I32 ) ,
157
+ ty:: Int ( IntTy :: I64 ) | ty:: Uint ( UintTy :: U64 ) => Some ( InlineAsmType :: I64 ) ,
158
+ ty:: Int ( IntTy :: I128 ) | ty:: Uint ( UintTy :: U128 ) => Some ( InlineAsmType :: I128 ) ,
159
+ ty:: Int ( IntTy :: Isize ) | ty:: Uint ( UintTy :: Usize ) => Some ( asm_ty_isize) ,
160
+ ty:: Float ( FloatTy :: F32 ) => Some ( InlineAsmType :: F32 ) ,
161
+ ty:: Float ( FloatTy :: F64 ) => Some ( InlineAsmType :: F64 ) ,
162
+ ty:: FnPtr ( _) => Some ( asm_ty_isize) ,
163
+ ty:: RawPtr ( ty:: TypeAndMut { ty, mutbl : _ } ) if self . is_thin_ptr_ty ( ty) => {
164
+ Some ( asm_ty_isize)
165
+ }
166
+ ty:: Adt ( adt, substs) if adt. repr . simd ( ) => {
167
+ let fields = & adt. non_enum_variant ( ) . fields ;
168
+ let elem_ty = fields[ 0 ] . ty ( self . tcx , substs) ;
169
+ match elem_ty. kind {
170
+ ty:: Never | ty:: Error => return None ,
171
+ ty:: Int ( IntTy :: I8 ) | ty:: Uint ( UintTy :: U8 ) => {
172
+ Some ( InlineAsmType :: VecI8 ( fields. len ( ) as u64 ) )
173
+ }
174
+ ty:: Int ( IntTy :: I16 ) | ty:: Uint ( UintTy :: U16 ) => {
175
+ Some ( InlineAsmType :: VecI16 ( fields. len ( ) as u64 ) )
176
+ }
177
+ ty:: Int ( IntTy :: I32 ) | ty:: Uint ( UintTy :: U32 ) => {
178
+ Some ( InlineAsmType :: VecI32 ( fields. len ( ) as u64 ) )
179
+ }
180
+ ty:: Int ( IntTy :: I64 ) | ty:: Uint ( UintTy :: U64 ) => {
181
+ Some ( InlineAsmType :: VecI64 ( fields. len ( ) as u64 ) )
182
+ }
183
+ ty:: Int ( IntTy :: I128 ) | ty:: Uint ( UintTy :: U128 ) => {
184
+ Some ( InlineAsmType :: VecI128 ( fields. len ( ) as u64 ) )
185
+ }
186
+ ty:: Int ( IntTy :: Isize ) | ty:: Uint ( UintTy :: Usize ) => {
187
+ Some ( match self . tcx . sess . target . ptr_width {
188
+ 16 => InlineAsmType :: VecI16 ( fields. len ( ) as u64 ) ,
189
+ 32 => InlineAsmType :: VecI32 ( fields. len ( ) as u64 ) ,
190
+ 64 => InlineAsmType :: VecI64 ( fields. len ( ) as u64 ) ,
191
+ _ => unreachable ! ( ) ,
192
+ } )
193
+ }
194
+ ty:: Float ( FloatTy :: F32 ) => Some ( InlineAsmType :: VecF32 ( fields. len ( ) as u64 ) ) ,
195
+ ty:: Float ( FloatTy :: F64 ) => Some ( InlineAsmType :: VecF64 ( fields. len ( ) as u64 ) ) ,
196
+ _ => None ,
197
+ }
198
+ }
199
+ _ => None ,
200
+ } ;
201
+ let asm_ty = match asm_ty {
202
+ Some ( asm_ty) => asm_ty,
203
+ None => {
204
+ let msg = & format ! ( "cannot use value of type `{}` for inline assembly" , ty) ;
205
+ let mut err = self . tcx . sess . struct_span_err ( expr. span , msg) ;
206
+ err. note (
207
+ "only integers, floats, SIMD vectors, pointers and function pointers \
208
+ can be used as arguments for inline assembly",
209
+ ) ;
210
+ err. emit ( ) ;
211
+ return None ;
212
+ }
213
+ } ;
214
+
215
+ // Check that the type implements Copy. The only case where this can
216
+ // possibly fail is for SIMD types which don't #[derive(Copy)].
217
+ if !ty. is_copy_modulo_regions ( self . tcx , self . param_env , DUMMY_SP ) {
218
+ let msg = "arguments for inline assembly must be copyable" ;
219
+ let mut err = self . tcx . sess . struct_span_err ( expr. span , msg) ;
220
+ err. note ( & format ! ( "`{}` does not implement the Copy trait" , ty) ) ;
221
+ err. emit ( ) ;
222
+ }
223
+
224
+ // Ideally we wouldn't need to do this, but LLVM's register allocator
225
+ // really doesn't like it when tied operands have different types.
226
+ //
227
+ // This is purely an LLVM limitation, but we have to live with it since
228
+ // there is no way to hide this with implicit conversions.
229
+ //
230
+ // For the purposes of this check we only look at the `InlineAsmType`,
231
+ // which means that pointers and integers are treated as identical (modulo
232
+ // size).
233
+ if let Some ( ( in_expr, Some ( in_asm_ty) ) ) = tied_input {
234
+ if in_asm_ty != asm_ty {
235
+ let msg = & format ! ( "incompatible types for asm inout argument" ) ;
236
+ let mut err = self . tcx . sess . struct_span_err ( vec ! [ in_expr. span, expr. span] , msg) ;
237
+ err. span_label (
238
+ in_expr. span ,
239
+ & format ! ( "type `{}`" , self . tables. expr_ty_adjusted( in_expr) ) ,
240
+ ) ;
241
+ err. span_label ( expr. span , & format ! ( "type `{}`" , ty) ) ;
242
+ err. note ( "asm inout arguments must have the same type" ) ;
243
+ err. note ( "unless they are both pointers or integers of the same size" ) ;
244
+ err. emit ( ) ;
245
+ }
246
+
247
+ // All of the later checks have already been done on the input, so
248
+ // let's not emit errors and warnings twice.
249
+ return Some ( asm_ty) ;
250
+ }
251
+
252
+ // Check the type against the list of types supported by the selected
253
+ // register class.
254
+ let asm_arch = self . tcx . sess . asm_arch . unwrap ( ) ;
255
+ let reg_class = reg. reg_class ( ) ;
256
+ let supported_tys = reg_class. supported_types ( asm_arch) ;
257
+ let feature = match supported_tys. iter ( ) . find ( |& & ( t, _) | t == asm_ty) {
258
+ Some ( ( _, feature) ) => feature,
259
+ None => {
260
+ let msg = & format ! ( "type `{}` cannot be used with this register class" , ty) ;
261
+ let mut err = self . tcx . sess . struct_span_err ( expr. span , msg) ;
262
+ let supported_tys: Vec < _ > =
263
+ supported_tys. iter ( ) . map ( |( t, _) | t. to_string ( ) ) . collect ( ) ;
264
+ err. note ( & format ! (
265
+ "register class `{}` supports these types: {}" ,
266
+ reg_class. name( ) ,
267
+ supported_tys. join( ", " ) ,
268
+ ) ) ;
269
+ err. emit ( ) ;
270
+ return Some ( asm_ty) ;
271
+ }
272
+ } ;
273
+
274
+ // Check whether the selected type requires a target feature.
275
+ if let Some ( feature) = feature {
276
+ if !self . tcx . sess . target_features . contains ( & Symbol :: intern ( feature) ) {
277
+ let msg = & format ! ( "`{}` target feature is not enabled" , feature) ;
278
+ let mut err = self . tcx . sess . struct_span_err ( expr. span , msg) ;
279
+ err. note ( & format ! (
280
+ "this is required to use type `{}` with register class `{}`" ,
281
+ ty,
282
+ reg_class. name( ) ,
283
+ ) ) ;
284
+ err. emit ( ) ;
285
+ return Some ( asm_ty) ;
286
+ }
287
+ }
288
+
289
+ // Check whether a modifier is suggested for using this type.
290
+ if let Some ( ( suggested_modifier, suggested_result, switch_reg_class) ) =
291
+ reg_class. suggest_modifier ( asm_arch, asm_ty)
292
+ {
293
+ // Search for any use of this operand without a modifier and emit
294
+ // the suggestion for them.
295
+ let mut spans = vec ! [ ] ;
296
+ for piece in template {
297
+ if let & InlineAsmTemplatePiece :: Placeholder { operand_idx, modifier, span } = piece
298
+ {
299
+ if operand_idx == idx && modifier. is_none ( ) {
300
+ spans. push ( span) ;
301
+ }
302
+ }
303
+ }
304
+ if !spans. is_empty ( ) {
305
+ let ( default_modifier, default_result) =
306
+ reg_class. default_modifier ( asm_arch) . unwrap ( ) ;
307
+ self . tcx . struct_span_lint_hir (
308
+ lint:: builtin:: ASM_SUB_REGISTER ,
309
+ expr. hir_id ,
310
+ spans,
311
+ |lint| {
312
+ let msg = "formatting may not be suitable for sub-register argument" ;
313
+ let mut err = lint. build ( msg) ;
314
+ err. span_label ( expr. span , "for this argument" ) ;
315
+ if let Some ( switch_reg_class) = switch_reg_class {
316
+ err. help ( & format ! (
317
+ "use the `{}` modifier with the `{}` register class \
318
+ to have the register formatted as `{}`",
319
+ suggested_modifier, switch_reg_class, suggested_result,
320
+ ) ) ;
321
+ } else {
322
+ err. help ( & format ! (
323
+ "use the `{}` modifier to have the register formatted as `{}`" ,
324
+ suggested_modifier, suggested_result,
325
+ ) ) ;
326
+ }
327
+ err. help ( & format ! (
328
+ "or use the `{}` modifier to keep the default formatting of `{}`" ,
329
+ default_modifier, default_result,
330
+ ) ) ;
331
+ err. emit ( ) ;
332
+ } ,
333
+ ) ;
334
+ }
335
+ }
336
+
337
+ Some ( asm_ty)
338
+ }
339
+
340
+ fn check_asm ( & self , asm : & hir:: InlineAsm < ' tcx > ) {
341
+ for ( idx, op) in asm. operands . iter ( ) . enumerate ( ) {
342
+ match * op {
343
+ hir:: InlineAsmOperand :: In { reg, ref expr } => {
344
+ self . check_asm_operand_type ( idx, reg, expr, asm. template , None ) ;
345
+ }
346
+ hir:: InlineAsmOperand :: Out { reg, late : _, ref expr } => {
347
+ if let Some ( expr) = expr {
348
+ self . check_asm_operand_type ( idx, reg, expr, asm. template , None ) ;
349
+ }
350
+ }
351
+ hir:: InlineAsmOperand :: InOut { reg, late : _, ref expr } => {
352
+ self . check_asm_operand_type ( idx, reg, expr, asm. template , None ) ;
353
+ }
354
+ hir:: InlineAsmOperand :: SplitInOut { reg, late : _, ref in_expr, ref out_expr } => {
355
+ let in_ty = self . check_asm_operand_type ( idx, reg, in_expr, asm. template , None ) ;
356
+ if let Some ( out_expr) = out_expr {
357
+ self . check_asm_operand_type (
358
+ idx,
359
+ reg,
360
+ out_expr,
361
+ asm. template ,
362
+ Some ( ( in_expr, in_ty) ) ,
363
+ ) ;
364
+ }
365
+ }
366
+ hir:: InlineAsmOperand :: Const { ref expr } => {
367
+ let ty = self . tables . expr_ty_adjusted ( expr) ;
368
+ match ty. kind {
369
+ ty:: Int ( _) | ty:: Uint ( _) | ty:: Float ( _) => { }
370
+ _ => {
371
+ let msg =
372
+ "asm `const` arguments must be integer or floating-point values" ;
373
+ self . tcx . sess . span_err ( expr. span , msg) ;
374
+ }
375
+ }
376
+ }
377
+ hir:: InlineAsmOperand :: Sym { .. } => { }
378
+ }
379
+ }
380
+ }
122
381
}
123
382
124
383
impl Visitor < ' tcx > for ItemVisitor < ' tcx > {
@@ -146,19 +405,23 @@ impl Visitor<'tcx> for ExprVisitor<'tcx> {
146
405
}
147
406
148
407
fn visit_expr ( & mut self , expr : & ' tcx hir:: Expr < ' tcx > ) {
149
- let res = if let hir :: ExprKind :: Path ( ref qpath ) = expr. kind {
150
- self . tables . qpath_res ( qpath , expr . hir_id )
151
- } else {
152
- Res :: Err
153
- } ;
154
- if let Res :: Def ( DefKind :: Fn , did ) = res {
155
- if self . def_id_is_transmute ( did ) {
156
- let typ = self . tables . node_type ( expr . hir_id ) ;
157
- let sig = typ . fn_sig ( self . tcx ) ;
158
- let from = sig . inputs ( ) . skip_binder ( ) [ 0 ] ;
159
- let to = * sig . output ( ) . skip_binder ( ) ;
160
- self . check_transmute ( expr . span , from , to ) ;
408
+ match expr. kind {
409
+ hir :: ExprKind :: Path ( ref qpath ) => {
410
+ let res = self . tables . qpath_res ( qpath , expr . hir_id ) ;
411
+ if let Res :: Def ( DefKind :: Fn , did ) = res {
412
+ if self . def_id_is_transmute ( did ) {
413
+ let typ = self . tables . node_type ( expr . hir_id ) ;
414
+ let sig = typ . fn_sig ( self . tcx ) ;
415
+ let from = sig . inputs ( ) . skip_binder ( ) [ 0 ] ;
416
+ let to = * sig . output ( ) . skip_binder ( ) ;
417
+ self . check_transmute ( expr . span , from , to ) ;
418
+ }
419
+ }
161
420
}
421
+
422
+ hir:: ExprKind :: InlineAsm ( asm) => self . check_asm ( asm) ,
423
+
424
+ _ => { }
162
425
}
163
426
164
427
intravisit:: walk_expr ( self , expr) ;
0 commit comments