58
58
add_tfunc (throw, 1 , 1 , (@nospecialize (x)) -> Bottom, 0 )
59
59
60
60
# the inverse of typeof_tfunc
61
- # returns (type, isexact)
61
+ # returns (type, isexact, isconcrete )
62
62
# if isexact is false, the actual runtime type may (will) be a subtype of t
63
+ # if isconcrete is true, the actual runtime type is definitely concrete (unreachable if not valid as a typeof)
63
64
function instanceof_tfunc (@nospecialize (t))
64
65
if isa (t, Const)
65
66
if isa (t. val, Type)
66
- return t. val, true
67
+ return t. val, true , isconcretetype (t . val)
67
68
end
68
- return Bottom, true
69
+ return Bottom, true , false # runtime throws on non-Type
69
70
end
70
71
t = widenconst (t)
71
- if t === Bottom || t === typeof (Bottom) || typeintersect (t, Type) === Bottom
72
- return Bottom, true
72
+ if t === Bottom
73
+ return Bottom, true , true # runtime unreachable
74
+ elseif t === typeof (Bottom) || typeintersect (t, Type) === Bottom
75
+ return Bottom, true , false # literal Bottom or non-Type
73
76
elseif isType (t)
74
77
tp = t. parameters[1 ]
75
- return tp, ! has_free_typevars (tp)
78
+ return tp, ! has_free_typevars (tp), isconcretetype (tp)
76
79
elseif isa (t, UnionAll)
77
80
t′ = unwrap_unionall (t)
78
- t′′, isexact = instanceof_tfunc (t′)
81
+ t′′, isexact, isconcrete = instanceof_tfunc (t′)
79
82
tr = rewrap_unionall (t′′, t)
80
- if t′′ isa DataType && ! has_free_typevars (tr)
83
+ if t′′ isa DataType && t′′ . name != = Tuple . name && ! has_free_typevars (tr)
81
84
# a real instance must be within the declared bounds of the type,
82
85
# so we can intersect with the original wrapper.
83
86
tr = typeintersect (tr, t′′. name. wrapper)
87
+ isconcrete = ! t′′. abstract
88
+ if tr === Union{}
89
+ # runtime unreachable (our inference Type{T} where S is
90
+ # uninhabited with any runtime T that exists)
91
+ isexact = true
92
+ end
84
93
end
85
- return tr, isexact
94
+ return tr, isexact, isconcrete
86
95
elseif isa (t, Union)
87
- ta, isexact_a = instanceof_tfunc (t. a)
88
- tb, isexact_b = instanceof_tfunc (t. b)
89
- ta === Union{} && return tb, isexact_b
90
- tb === Union{} && return ta, isexact_a
91
- ta == tb && return ta, isexact_a && isexact_b
92
- return Union{ta, tb}, false # at runtime, will be exactly one of these
93
- end
94
- return Any, false
96
+ ta, isexact_a, isconcrete_a = instanceof_tfunc (t. a)
97
+ tb, isexact_b, isconcrete_b = instanceof_tfunc (t. b)
98
+ isconcrete = isconcrete_a && isconcrete_b
99
+ # most users already handle the Union case, so here we assume that
100
+ # `isexact` only cares about the answers where there's actually a Type
101
+ # (and assuming other cases causing runtime errors)
102
+ ta === Union{} && return tb, isexact_b, isconcrete
103
+ tb === Union{} && return ta, isexact_a, isconcrete
104
+ return Union{ta, tb}, false , isconcrete # at runtime, will be exactly one of these
105
+ end
106
+ return Any, false , false
95
107
end
96
108
bitcast_tfunc (@nospecialize (t), @nospecialize (x)) = instanceof_tfunc (t)[1 ]
97
109
math_tfunc (@nospecialize (x)) = widenconst (x)
@@ -294,38 +306,41 @@ add_tfunc(isdefined, 2, 2, isdefined_tfunc, 1)
294
306
295
307
function sizeof_nothrow (@nospecialize (x))
296
308
if isa (x, Const)
297
- x = x. val
298
- if ! isa (x, Type) || x === DataType
309
+ if ! isa (x. val, Type) || x. val === DataType
299
310
return true
300
311
end
301
312
elseif isa (x, Conditional)
302
313
return true
303
314
end
304
- if isa (x, Union)
305
- return sizeof_nothrow (x. a) && sizeof_nothrow (x. b)
315
+ xu = unwrap_unionall (x)
316
+ if isa (xu, Union)
317
+ return sizeof_nothrow (rewrap_unionall (xu. a, x)) &&
318
+ sizeof_nothrow (rewrap_unionall (xu. b, x))
306
319
end
307
- t, exact = instanceof_tfunc (x)
308
- if ! exact
309
- # Could always be bottom at runtime, which throws
310
- return false
320
+ t, exact, isconcrete = instanceof_tfunc (x)
321
+ if t === Bottom
322
+ # x must be an instance (not a Type) or is the Bottom type object
323
+ x = widenconst (x)
324
+ return typeintersect (x, Type) === Union{}
311
325
end
312
- if t != = Bottom
313
- t === DataType && return true
314
- x = t
315
- x = unwrap_unionall (x)
316
- if isa (x, Union)
317
- isinline, sz, _ = uniontype_layout (x)
318
- return isinline
326
+ x = unwrap_unionall (t)
327
+ if isconcrete
328
+ if isa (x, DataType) && x. layout != C_NULL
329
+ # there's just a few concrete types with an opaque layout
330
+ (datatype_nfields (x) == 0 && ! datatype_pointerfree (x)) && return false
319
331
end
320
- isa (x, DataType) || return false
321
- x. layout == C_NULL && return false
322
- (datatype_nfields (x) == 0 && ! datatype_pointerfree (x)) && return false
323
- return true
324
- else
325
- x = widenconst (x)
326
- x === DataType && return false
327
- return isconcretetype (x) || isprimitivetype (x)
332
+ return true # these must always have a size of these
328
333
end
334
+ exact || return false # Could always be the type Bottom at runtime, for example, which throws
335
+ t === DataType && return true # DataType itself has a size
336
+ if isa (x, Union)
337
+ isinline, sz, _ = uniontype_layout (x)
338
+ return isinline # even any subset of this union would have a size
339
+ end
340
+ isa (x, DataType) || return false
341
+ x. layout == C_NULL && return false
342
+ (datatype_nfields (x) == 0 && ! datatype_pointerfree (x)) && return false # is-layout-opaque
343
+ return true
329
344
end
330
345
331
346
function _const_sizeof (@nospecialize (x))
@@ -346,8 +361,10 @@ function sizeof_tfunc(@nospecialize(x),)
346
361
isa (x, Const) && return _const_sizeof (x. val)
347
362
isa (x, Conditional) && return _const_sizeof (Bool)
348
363
isconstType (x) && return _const_sizeof (x. parameters[1 ])
349
- if isa (x, Union)
350
- return tmerge (sizeof_tfunc (x. a), sizeof_tfunc (x. b))
364
+ xu = unwrap_unionall (x)
365
+ if isa (xu, Union)
366
+ return tmerge (sizeof_tfunc (rewrap_unionall (xu. a, x)),
367
+ sizeof_tfunc (rewrap_unionall (xu. b, x)))
351
368
end
352
369
# Core.sizeof operates on either a type or a value. First check which
353
370
# case we're in.
@@ -356,9 +373,9 @@ function sizeof_tfunc(@nospecialize(x),)
356
373
# The value corresponding to `x` at runtime could be a type.
357
374
# Normalize the query to ask about that type.
358
375
x = unwrap_unionall (t)
359
- if isa (x, Union)
376
+ if exact && isa (x, Union)
360
377
isinline, sz, _ = uniontype_layout (x)
361
- return isinline ? Const (Int (sz)) : (exact ? Bottom : Int)
378
+ return isinline ? Const (Int (sz)) : Bottom
362
379
end
363
380
isa (x, DataType) || return Int
364
381
(isconcretetype (x) || isprimitivetype (x)) && return _const_sizeof (x)
@@ -1531,18 +1548,18 @@ function intrinsic_nothrow(f::IntrinsicFunction, argtypes::Array{Any, 1})
1531
1548
return argtypes[1 ] ⊑ Array
1532
1549
end
1533
1550
if f === Intrinsics. bitcast
1534
- ty = instanceof_tfunc (argtypes[1 ])[ 1 ]
1551
+ ty, isexact, isconcrete = instanceof_tfunc (argtypes[1 ])
1535
1552
xty = widenconst (argtypes[2 ])
1536
- return isprimitivetype (ty) && isprimitivetype (xty) && ty . size === xty . size
1553
+ return isconcrete && isprimitivetype (ty) && isprimitivetype (xty) && Core . sizeof (ty) === Core . sizeof (xty)
1537
1554
end
1538
1555
if f in (Intrinsics. sext_int, Intrinsics. zext_int, Intrinsics. trunc_int,
1539
1556
Intrinsics. fptoui, Intrinsics. fptosi, Intrinsics. uitofp,
1540
1557
Intrinsics. sitofp, Intrinsics. fptrunc, Intrinsics. fpext)
1541
- # If !isexact , `ty` may be Union{} at runtime even if we have
1558
+ # If !isconcrete , `ty` may be Union{} at runtime even if we have
1542
1559
# isprimitivetype(ty).
1543
- ty, isexact = instanceof_tfunc (argtypes[1 ])
1560
+ ty, isexact, isconcrete = instanceof_tfunc (argtypes[1 ])
1544
1561
xty = widenconst (argtypes[2 ])
1545
- return isexact && isprimitivetype (ty) && isprimitivetype (xty)
1562
+ return isconcrete && isprimitivetype (ty) && isprimitivetype (xty)
1546
1563
end
1547
1564
# The remaining intrinsics are math/bits/comparison intrinsics. They work on all
1548
1565
# primitive types of the same type.
0 commit comments