Skip to content

Commit eea64c2

Browse files
authored
Merge pull request #37616 from JuliaLang/jn/36962-again
improve accuracy of ambiguity checking (take 2)
2 parents 46cf572 + 5788bbb commit eea64c2

File tree

8 files changed

+392
-240
lines changed

8 files changed

+392
-240
lines changed

base/reflection.jl

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,8 @@ Determine whether two methods `m1` and `m2` may be ambiguous for some call
13631363
signature. This test is performed in the context of other methods of the same
13641364
function; in isolation, `m1` and `m2` might be ambiguous, but if a third method
13651365
resolving the ambiguity has been defined, this returns `false`.
1366+
Alternatively, in isolation `m1` and `m2` might be ordered, but if a third
1367+
method cannot be sorted with them, they may cause an ambiguity together.
13661368
13671369
For parametric types, the `ambiguous_bottom` keyword argument controls whether
13681370
`Union{}` counts as an ambiguous intersection of type parameters – when `true`,
@@ -1389,27 +1391,87 @@ false
13891391
```
13901392
"""
13911393
function isambiguous(m1::Method, m2::Method; ambiguous_bottom::Bool=false)
1392-
# TODO: eagerly returning `morespecific` is wrong, and fails to consider
1393-
# the possibility of an ambiguity caused by a third method:
1394-
# see the precise algorithm in ml_matches for a more correct computation
1395-
if m1 === m2 || morespecific(m1.sig, m2.sig) || morespecific(m2.sig, m1.sig)
1396-
return false
1397-
end
1394+
m1 === m2 && return false
13981395
ti = typeintersect(m1.sig, m2.sig)
1399-
(ti <: m1.sig && ti <: m2.sig) || return false # XXX: completely wrong, obviously
14001396
ti === Bottom && return false
1401-
if !ambiguous_bottom
1402-
has_bottom_parameter(ti) && return false
1403-
end
1404-
matches = _methods_by_ftype(ti, -1, typemax(UInt))
1405-
for match in matches
1406-
m = match.method
1407-
m === m1 && continue
1408-
m === m2 && continue
1409-
if ti <: m.sig && morespecific(m.sig, m1.sig) && morespecific(m.sig, m2.sig)
1397+
function inner(ti)
1398+
ti === Bottom && return false
1399+
if !ambiguous_bottom
1400+
has_bottom_parameter(ti) && return false
1401+
end
1402+
min = UInt[typemin(UInt)]
1403+
max = UInt[typemax(UInt)]
1404+
has_ambig = Int32[0]
1405+
ms = _methods_by_ftype(ti, -1, typemax(UInt), true, min, max, has_ambig)
1406+
has_ambig[] == 0 && return false
1407+
if !ambiguous_bottom
1408+
filter!(ms) do m
1409+
return !has_bottom_parameter(m.spec_types)
1410+
end
1411+
end
1412+
# if ml-matches reported the existence of an ambiguity over their
1413+
# intersection, see if both m1 and m2 may be involved in it
1414+
have_m1 = have_m2 = false
1415+
for match in ms
1416+
m = match.method
1417+
m === m1 && (have_m1 = true)
1418+
m === m2 && (have_m2 = true)
1419+
end
1420+
if !have_m1 || !have_m2
1421+
# ml-matches did not need both methods to expose the reported ambiguity
1422+
return false
1423+
end
1424+
if !ambiguous_bottom
1425+
# since we're intentionally ignoring certain ambiguities (via the
1426+
# filter call above), see if we can now declare the intersection fully
1427+
# covered even though it is partially ambiguous over Union{} as a type
1428+
# parameter somewhere
1429+
minmax = nothing
1430+
for match in ms
1431+
m = match.method
1432+
match.fully_covers || continue
1433+
if minmax === nothing || morespecific(m.sig, minmax.sig)
1434+
minmax = m
1435+
end
1436+
end
1437+
if minmax === nothing
1438+
return true
1439+
end
1440+
for match in ms
1441+
m = match.method
1442+
m === minmax && continue
1443+
if match.fully_covers
1444+
if !morespecific(minmax.sig, m.sig)
1445+
return true
1446+
end
1447+
else
1448+
if morespecific(m.sig, minmax.sig)
1449+
return true
1450+
end
1451+
end
1452+
end
14101453
return false
14111454
end
1455+
return true
1456+
end
1457+
if !(ti <: m1.sig && ti <: m2.sig)
1458+
# When type-intersection fails, it's often also not commutative. Thus
1459+
# checking the reverse may allow detecting ambiguity solutions
1460+
# correctly in more cases (and faster).
1461+
ti2 = typeintersect(m2.sig, m1.sig)
1462+
if ti2 <: m1.sig && ti2 <: m2.sig
1463+
ti = ti2
1464+
elseif ti != ti2
1465+
# TODO: this would be the correct way to handle this case, but
1466+
# people complained so we don't do it
1467+
# inner(ti2) || return false
1468+
return false # report that the type system failed to decide if it was ambiguous by saying they definitely aren't
1469+
else
1470+
return false # report that the type system failed to decide if it was ambiguous by saying they definitely aren't
1471+
end
14121472
end
1473+
inner(ti) || return false
1474+
# otherwise type-intersection reported an ambiguity we couldn't solve
14131475
return true
14141476
end
14151477

src/clangsa/GCChecker.cpp

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

0 commit comments

Comments
 (0)