Skip to content

Commit bd52a95

Browse files
Fix some overly optimistic fastpaths in subtyping (#25796)
1 parent e36194a commit bd52a95

File tree

7 files changed

+62
-27
lines changed

7 files changed

+62
-27
lines changed

src/datatype.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void)
9696
t->zeroinit = 0;
9797
t->isinlinealloc = 0;
9898
t->has_concrete_subtype = 1;
99+
t->cached_by_hash = 0;
99100
t->layout = NULL;
100101
t->names = NULL;
101102
t->types = NULL;

src/dump.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) JL_
278278
| (dt->isbitstype << 3)
279279
| (dt->zeroinit << 4)
280280
| (dt->isinlinealloc << 5)
281-
| (dt->has_concrete_subtype << 6));
281+
| (dt->has_concrete_subtype << 6)
282+
| (dt->cached_by_hash << 7));
282283
if (!dt->abstract) {
283284
write_uint16(s->s, dt->ninitialized);
284285
}
@@ -1174,6 +1175,7 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v
11741175
dt->zeroinit = (memflags >> 4) & 1;
11751176
dt->isinlinealloc = (memflags >> 5) & 1;
11761177
dt->has_concrete_subtype = (memflags >> 6) & 1;
1178+
dt->cached_by_hash = (memflags >> 7) & 1;
11771179
dt->types = NULL;
11781180
dt->parameters = NULL;
11791181
dt->name = NULL;

src/jltypes.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ static int typekey_eq(jl_datatype_t *tt, jl_value_t **key, size_t n)
568568
// require exact same Type{T}. see e.g. issue #22842
569569
if (jl_is_type_type(tj) || jl_is_type_type(kj))
570570
return 0;
571-
if (jl_is_concrete_type(tj) || jl_is_concrete_type(kj))
571+
if ((jl_is_concrete_type(tj) || jl_is_concrete_type(kj)) &&
572+
jl_type_equality_is_identity(tj, kj))
572573
return 0;
573574
if (!jl_types_equal(tj, kj))
574575
return 0;
@@ -860,6 +861,17 @@ jl_datatype_t *jl_lookup_cache_type_(jl_datatype_t *type)
860861
return (jl_datatype_t*)lookup_type(type->name, key, n);
861862
}
862863

864+
int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2)
865+
{
866+
if (t1 == t2)
867+
return 1;
868+
if (!jl_is_datatype(t1) || !jl_is_datatype(t2))
869+
return 0;
870+
jl_datatype_t *dt1 = (jl_datatype_t *) t1;
871+
jl_datatype_t *dt2 = (jl_datatype_t *) t2;
872+
873+
return dt1->cached_by_hash == dt2->cached_by_hash;
874+
}
863875

864876
// type instantiation
865877

@@ -1155,6 +1167,7 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable)
11551167
if (dt->name == jl_type_typename)
11561168
cacheable = 0; // the cache for Type ignores parameter normalization, so it can't be used as a regular hash
11571169
dt->hash = typekey_hash(dt->name, jl_svec_data(dt->parameters), l, cacheable);
1170+
dt->cached_by_hash = cacheable ? (typekey_hash(dt->name, jl_svec_data(dt->parameters), l, 0) != 0) : (dt->hash != 0);
11581171
}
11591172

11601173
static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, size_t np)
@@ -1880,7 +1893,7 @@ void jl_init_types(void) JL_GC_DISABLED
18801893
jl_datatype_type->name->wrapper = (jl_value_t*)jl_datatype_type;
18811894
jl_datatype_type->super = (jl_datatype_t*)jl_type_type;
18821895
jl_datatype_type->parameters = jl_emptysvec;
1883-
jl_datatype_type->name->names = jl_perm_symsvec(19,
1896+
jl_datatype_type->name->names = jl_perm_symsvec(20,
18841897
"name",
18851898
"super",
18861899
"parameters",
@@ -1899,8 +1912,9 @@ void jl_init_types(void) JL_GC_DISABLED
18991912
"isbitstype",
19001913
"zeroinit",
19011914
"isinlinealloc",
1902-
"has_concrete_subtype");
1903-
jl_datatype_type->types = jl_svec(19,
1915+
"has_concrete_subtype",
1916+
"cached_by_hash");
1917+
jl_datatype_type->types = jl_svec(20,
19041918
jl_typename_type,
19051919
jl_datatype_type,
19061920
jl_simplevector_type,
@@ -1909,7 +1923,7 @@ void jl_init_types(void) JL_GC_DISABLED
19091923
jl_any_type, jl_any_type, jl_any_type, jl_any_type, // properties
19101924
jl_any_type, jl_any_type, jl_any_type, jl_any_type,
19111925
jl_any_type, jl_any_type, jl_any_type, jl_any_type,
1912-
jl_any_type);
1926+
jl_any_type, jl_any_type);
19131927
jl_datatype_type->abstract = 0;
19141928
// NOTE: types are not actually mutable, but we want to ensure they are heap-allocated with stable addresses
19151929
jl_datatype_type->mutabl = 1;
@@ -2019,10 +2033,12 @@ void jl_init_types(void) JL_GC_DISABLED
20192033
jl_anytuple_type->isconcretetype = 0;
20202034
jl_anytuple_type->layout = NULL;
20212035
jl_anytuple_type->size = 0;
2036+
jl_anytuple_type->cached_by_hash = 0;
20222037

20232038
jl_tvar_t *tttvar = tvar("T");
20242039
((jl_datatype_t*)jl_type_type)->parameters = jl_svec(1, tttvar);
20252040
((jl_datatype_t*)jl_type_type)->hasfreetypevars = 1;
2041+
((jl_datatype_t*)jl_type_type)->cached_by_hash = 0;
20262042
jl_type_typename->wrapper = jl_new_struct(jl_unionall_type, tttvar, (jl_value_t*)jl_type_type);
20272043
jl_type_type = (jl_unionall_t*)jl_type_typename->wrapper;
20282044

@@ -2482,6 +2498,7 @@ void jl_init_types(void) JL_GC_DISABLED
24822498
jl_svecset(jl_datatype_type->types, 16, jl_bool_type);
24832499
jl_svecset(jl_datatype_type->types, 17, jl_bool_type);
24842500
jl_svecset(jl_datatype_type->types, 18, jl_bool_type);
2501+
jl_svecset(jl_datatype_type->types, 19, jl_bool_type);
24852502
jl_svecset(jl_typename_type->types, 1, jl_module_type);
24862503
jl_svecset(jl_typename_type->types, 6, jl_long_type);
24872504
jl_svecset(jl_typename_type->types, 3, jl_type_type);

src/julia.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ typedef struct _jl_datatype_t {
457457
uint8_t zeroinit; // if one or more fields requires zero-initialization
458458
uint8_t isinlinealloc; // if this is allocated inline
459459
uint8_t has_concrete_subtype; // If clear, no value will have this datatype
460+
uint8_t cached_by_hash; // stored in hash-based set cache (instead of linear cache)
460461
} jl_datatype_t;
461462

462463
typedef struct {
@@ -1216,6 +1217,7 @@ JL_DLLEXPORT int jl_egal(jl_value_t *a JL_MAYBE_UNROOTED, jl_value_t *b JL_MAYBE
12161217
JL_DLLEXPORT uintptr_t jl_object_id(jl_value_t *v) JL_NOTSAFEPOINT;
12171218

12181219
// type predicates and basic operations
1220+
JL_DLLEXPORT int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT;
12191221
JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) JL_NOTSAFEPOINT;
12201222
JL_DLLEXPORT int jl_has_typevar(jl_value_t *t, jl_tvar_t *v) JL_NOTSAFEPOINT;
12211223
JL_DLLEXPORT int jl_has_typevar_from_unionall(jl_value_t *t, jl_unionall_t *ua);

src/subtype.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ static int obviously_unequal(jl_value_t *a, jl_value_t *b)
247247
if (ad->name != bd->name)
248248
return 1;
249249
int istuple = (ad->name == jl_tuple_typename);
250-
if (jl_is_concrete_type(a) || jl_is_concrete_type(b)) {
250+
if ((jl_is_concrete_type(a) || jl_is_concrete_type(b)) &&
251+
jl_type_equality_is_identity(a, b)) {
251252
if (!istuple && ad->name != jl_type_typename) // HACK: can't properly normalize Tuple{Float64} == Tuple{<:Float64} like types or Type{T} types
252253
return 1;
253254
}
@@ -313,13 +314,11 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity)
313314
return 0;
314315
if (specificity && a == (jl_value_t*)jl_typeofbottom_type)
315316
return 0;
316-
// TODO: this would be a nice fast-path to have, unfortuanately,
317-
// datatype allocation fails to correctly hash-cons them
318-
// and the subtyping tests include tests for this case
319-
//if (jl_is_concrete_type(a) && jl_is_concrete_type(b) &&
320-
// (((jl_datatype_t*)a)->name != jl_tuple_typename ||
321-
// ((jl_datatype_t*)b)->name != jl_tuple_typename))
322-
// return 1;
317+
if (jl_is_concrete_type(a) && jl_is_concrete_type(b) &&
318+
jl_type_equality_is_identity(a, b) &&
319+
(((jl_datatype_t*)a)->name != jl_tuple_typename ||
320+
((jl_datatype_t*)b)->name != jl_tuple_typename))
321+
return 1;
323322
if (jl_is_unionall(a)) a = jl_unwrap_unionall(a);
324323
if (jl_is_unionall(b)) b = jl_unwrap_unionall(b);
325324
if (jl_is_datatype(a) && jl_is_datatype(b)) {
@@ -2115,7 +2114,7 @@ JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t)
21152114
return 0;
21162115
}
21172116
}
2118-
if (jl_is_concrete_type(t))
2117+
if (jl_is_concrete_type(t) && jl_type_equality_is_identity(jl_typeof(x), t))
21192118
return 0;
21202119
return jl_subtype(jl_typeof(x), t);
21212120
}

test/core.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7239,6 +7239,17 @@ struct AVL35416{K,V}
72397239
end
72407240
@test AVL35416(Node35416{AVL35416{Integer,AbstractString},Int,String}()) isa AVL35416{Integer,AbstractString}
72417241

7242+
# issue #31696
7243+
foo31696(x::Int8, y::Int8) = 1
7244+
foo31696(x::T, y::T) where {T <: Int8} = 2
7245+
@test length(methods(foo31696)) == 1
7246+
let T1 = Tuple{Int8}, T2 = Tuple{T} where T<:Int8, a = T1[(1,)], b = T2[(1,)]
7247+
b .= a
7248+
@test b[1] == (1,)
7249+
a .= b
7250+
@test a[1] == (1,)
7251+
end
7252+
72427253
# issue #36104
72437254
module M36104
72447255
struct T36104

test/subtype.jl

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,18 @@ let (t, e) = intersection_env(Tuple{Union{Int,Int8}}, Tuple{T} where T)
13581358
@test e[1] isa TypeVar
13591359
end
13601360

1361+
# issue #25430
1362+
@test Vector{Tuple{Any}}() isa Vector{Tuple{>:Int}}
1363+
@test Vector{Tuple{>:Int}}() isa Vector{Tuple{Any}}
1364+
@test Vector{Tuple{Any}} == Vector{Tuple{>:Int}}
1365+
@test Vector{Vector{Tuple{Any}}} == Vector{Vector{Tuple{>:Int}}}
1366+
f25430(t::Vector{Tuple{Any}}) = true
1367+
g25430(t::Vector{Tuple{>:Int}}) = true
1368+
@test f25430(Vector{Tuple{>:Int}}())
1369+
@test g25430(Vector{Tuple{Any}}())
1370+
@testintersect(Vector{Tuple{>:Int}}, Vector{Tuple{Any}}, Vector{Tuple{Any}})
1371+
@testintersect(Vector{Vector{Tuple{>:Int}}}, Vector{Vector{Tuple{Any}}}, Vector{Vector{Tuple{Any}}})
1372+
13611373
# issue #24521
13621374
g24521(::T, ::T) where {T} = T
13631375
@test_throws MethodError g24521(Tuple{Any}, Tuple{T} where T)
@@ -1627,18 +1639,9 @@ end
16271639
@testintersect(Tuple{Type{<:AbstractVector{T}}, Int} where T,
16281640
Tuple{Type{Vector}, Any},
16291641
Union{})
1630-
#@testintersect(Tuple{Type{<:AbstractVector{T}}, Int} where T,
1631-
# Tuple{Type{Vector{T} where Int<:T<:Int}, Any},
1632-
# Tuple{Type{Vector{Int}}, Int})
1633-
@test_broken isequal(_type_intersect(Tuple{Type{<:AbstractVector{T}}, Int} where T,
1634-
Tuple{Type{Vector{T} where Int<:T<:Int}, Any}),
1635-
Tuple{Type{Vector{Int}}, Int})
1636-
@test isequal_type(_type_intersect(Tuple{Type{<:AbstractVector{T}}, Int} where T,
1637-
Tuple{Type{Vector{T} where Int<:T<:Int}, Any}),
1638-
Tuple{Type{Vector{Int}}, Int})
1639-
@test isequal_type(_type_intersect(Tuple{Type{Vector{T} where Int<:T<:Int}, Any},
1640-
Tuple{Type{<:AbstractVector{T}}, Int} where T),
1641-
Tuple{Type{Vector{Int}}, Int})
1642+
@testintersect(Tuple{Type{<:AbstractVector{T}}, Int} where T,
1643+
Tuple{Type{Vector{T} where Int<:T<:Int}, Any},
1644+
Tuple{Type{Vector{Int}}, Int})
16421645
let X = LinearAlgebra.Symmetric{T, S} where S<:(AbstractArray{U, 2} where U<:T) where T,
16431646
Y = Union{LinearAlgebra.Hermitian{T, S} where S<:(AbstractArray{U, 2} where U<:T) where T,
16441647
LinearAlgebra.Symmetric{T, S} where S<:(AbstractArray{U, 2} where U<:T) where T}

0 commit comments

Comments
 (0)