Skip to content

Commit 21ff6ee

Browse files
authored
ml-matches: update and improve ambiguity computation (#36962)
With the improved internal implementation of ambiguity testing, it's useful now to lean on that existing support more heavily and compute a lighter version here, fixing some issues with the previous one. Most of these new failures are likely to be type-intersection issues (#36951), which previously were excluded from analysis entirely.
1 parent 24b39ec commit 21ff6ee

File tree

8 files changed

+503
-211
lines changed

8 files changed

+503
-211
lines changed

base/reflection.jl

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,8 @@ Determine whether two methods `m1` and `m2` may be ambiguous for some call
13211321
signature. This test is performed in the context of other methods of the same
13221322
function; in isolation, `m1` and `m2` might be ambiguous, but if a third method
13231323
resolving the ambiguity has been defined, this returns `false`.
1324+
Alternatively, in isolation `m1` and `m2` might be ordered, but if a third
1325+
method cannot be sorted with them, they may cause an ambiguity together.
13241326
13251327
For parametric types, the `ambiguous_bottom` keyword argument controls whether
13261328
`Union{}` counts as an ambiguous intersection of type parameters – when `true`,
@@ -1347,27 +1349,82 @@ false
13471349
```
13481350
"""
13491351
function isambiguous(m1::Method, m2::Method; ambiguous_bottom::Bool=false)
1350-
# TODO: eagerly returning `morespecific` is wrong, and fails to consider
1351-
# the possibility of an ambiguity caused by a third method:
1352-
# see the precise algorithm in ml_matches for a more correct computation
1353-
if m1 === m2 || morespecific(m1.sig, m2.sig) || morespecific(m2.sig, m1.sig)
1354-
return false
1355-
end
1352+
m1 === m2 && return false
13561353
ti = typeintersect(m1.sig, m2.sig)
1357-
(ti <: m1.sig && ti <: m2.sig) || return false # XXX: completely wrong, obviously
13581354
ti === Bottom && return false
1359-
if !ambiguous_bottom
1360-
has_bottom_parameter(ti) && return false
1361-
end
1362-
matches = _methods_by_ftype(ti, -1, typemax(UInt))
1363-
for match in matches
1364-
m = match.method
1365-
m === m1 && continue
1366-
m === m2 && continue
1367-
if ti <: m.sig && morespecific(m.sig, m1.sig) && morespecific(m.sig, m2.sig)
1355+
function inner(ti)
1356+
ti === Bottom && return false
1357+
if !ambiguous_bottom
1358+
has_bottom_parameter(ti) && return false
1359+
end
1360+
min = UInt[typemin(UInt)]
1361+
max = UInt[typemax(UInt)]
1362+
has_ambig = Int32[0]
1363+
ms = _methods_by_ftype(ti, -1, typemax(UInt), true, min, max, has_ambig)
1364+
has_ambig[] == 0 && return false
1365+
if !ambiguous_bottom
1366+
filter!(ms) do m
1367+
return !has_bottom_parameter(m.spec_types)
1368+
end
1369+
end
1370+
# if ml-matches reported the existence of an ambiguity over their
1371+
# intersection, see if both m1 and m2 may be involved in it
1372+
have_m1 = have_m2 = false
1373+
for match in ms
1374+
m = match.method
1375+
m === m1 && (have_m1 = true)
1376+
m === m2 && (have_m2 = true)
1377+
end
1378+
if !have_m1 || !have_m2
1379+
# ml-matches did not need both methods to expose the reported ambiguity
13681380
return false
13691381
end
1382+
if !ambiguous_bottom
1383+
# since we're intentionally ignoring certain ambiguities (via the
1384+
# filter call above), see if we can now declare the intersection fully
1385+
# covered even though it is partially ambiguous over Union{} as a type
1386+
# parameter somewhere
1387+
minmax = nothing
1388+
for match in ms
1389+
m = match.method
1390+
match.fully_covers || continue
1391+
if minmax === nothing || morespecific(m.sig, minmax.sig)
1392+
minmax = m
1393+
end
1394+
end
1395+
if minmax === nothing
1396+
return true
1397+
end
1398+
for match in ms
1399+
m = match.method
1400+
m === minmax && continue
1401+
if match.fully_covers
1402+
if !morespecific(minmax.sig, m.sig)
1403+
return true
1404+
end
1405+
else
1406+
if morespecific(m.sig, minmax.sig)
1407+
return true
1408+
end
1409+
end
1410+
end
1411+
return false
1412+
end
1413+
return true
1414+
end
1415+
if !(ti <: m1.sig && ti <: m2.sig)
1416+
# When type-intersection fails, it's often also not commutative. Thus
1417+
# checking the reverse may allow detecting ambiguity solutions
1418+
# correctly in more cases (and faster).
1419+
ti2 = typeintersect(m2.sig, m1.sig)
1420+
if ti2 <: m1.sig && ti2 <: m2.sig
1421+
ti = ti2
1422+
elseif ti != ti2
1423+
inner(ti2) || return false
1424+
end
13701425
end
1426+
inner(ti) || return false
1427+
# otherwise type-intersection reported an ambiguity we couldn't solve
13711428
return true
13721429
end
13731430

src/clangsa/GCChecker.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,16 +1338,16 @@ bool GCChecker::evalCall(const CallExpr *CE,
13381338
return true;
13391339
} else if (name == "JL_GC_PUSH1" || name == "JL_GC_PUSH2" ||
13401340
name == "JL_GC_PUSH3" || name == "JL_GC_PUSH4" ||
1341-
name == "JL_GC_PUSH5" || name == "JL_GC_PUSH6") {
1341+
name == "JL_GC_PUSH5" || name == "JL_GC_PUSH6" ||
1342+
name == "JL_GC_PUSH7") {
13421343
ProgramStateRef State = C.getState();
13431344
// Transform slots to roots, transform values to rooted
13441345
unsigned NumArgs = CE->getNumArgs();
13451346
for (unsigned i = 0; i < NumArgs; ++i) {
13461347
SVal V = C.getSVal(CE->getArg(i));
13471348
auto MRV = V.getAs<loc::MemRegionVal>();
13481349
if (!MRV) {
1349-
report_error(C,
1350-
"JL_GC_PUSH with something other than a local variable");
1350+
report_error(C, "JL_GC_PUSH with something other than a local variable");
13511351
return true;
13521352
}
13531353
const MemRegion *Region = MRV->getRegion();

0 commit comments

Comments
 (0)