Skip to content

Commit ac520c6

Browse files
rikkimaxthewilsonator
authored andcommitted
Improve fast dfa so borrow checker supports ternary and if statements
1 parent b0591f2 commit ac520c6

8 files changed

Lines changed: 161 additions & 48 deletions

File tree

compiler/src/dmd/dfa/entry.d

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ void fastDFA(FuncDeclaration fd, Scope* sc)
100100
}
101101

102102
// Use these if statements for debugging specific things.
103-
//if (fd.ident.toString != "borrowOk5") return;
103+
//if (fd.ident.toString != "borrowInTernaryConditionInfect") return;
104104
//if (!(fd.ident.toString == "checkViaObjNullDeref" || fd.ident.toString == "typeNextIterate")) return;
105105
//if (fd.loc.linnum != 54) return;
106106
//if (fd.getModule.ident.toString != "doc") return;
@@ -234,10 +234,10 @@ void fastDFA(FuncDeclaration fd, Scope* sc)
234234

235235
dfaCommon.allocator.allVariables((DFAVar* var) {
236236
prefix("var");
237-
ob.printf(" %p base1=%p, base2=%p, dereferenceVar=%p, oldestLifeTimeAllowedDepth=%d<%d, writeCount=%d, unmodel=%d, isScope=%d, isByRef=%d, mayEscapeInitialValue=%d",
238-
var, var.base1, var.base2, var.dereferenceVar, var.oldestLifeTimeAllowedDepth,
239-
var.youngestLifeTimeAllowedDepth, var.writeCount, var.unmodellable,
240-
var.isScope, var.isByRef, var.mayEscapeInitialValue);
237+
ob.printf(" %p base1=%p, base2=%p, dereferenceVar=%p, storageFor=%p, oldestLifeTimeAllowedDepth=%d<%d, writeCount=%d, unmodel=%d, isScope=%d, isByRef=%d, mayEscapeInitialValue=%d",
238+
var, var.base1, var.base2, var.dereferenceVar, var.storageFor,
239+
var.oldestLifeTimeAllowedDepth, var.youngestLifeTimeAllowedDepth, var.writeCount,
240+
var.unmodellable, var.isScope, var.isByRef, var.mayEscapeInitialValue);
241241

242242
if (var.var !is null)
243243
{

compiler/src/dmd/dfa/fast/analysis.d

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,15 @@ struct DFAAnalyzer
11891189
DFAScope* sideEffectScope = dfaCommon.getSideEffectScope();
11901190
DFAScopeVar* scv = sideEffectScope.getScopeVar(root.storageFor);
11911191

1192-
if (argListItem.paramType is null || argListItem.paramType.isTypeMutable)
1192+
version(none)
1193+
{
1194+
printf("walking root=%p, cell=%p\n", root, lr.findConsequence(root.storageFor));
1195+
}
1196+
1197+
// Check to see if the object is the storage for a variable that we can model.
1198+
// If so we probably already handled it with seePointer, so if we were to do it again it would be a duplicate.
1199+
if (lr.findConsequence(root.storageFor) is null
1200+
&& (argListItem.paramType is null || argListItem.paramType.isTypeMutable))
11931201
seeWrite(root.storageFor, scv.lr, loc, silenceWriteError);
11941202
}
11951203
});
@@ -1512,7 +1520,6 @@ struct DFAAnalyzer
15121520
DFALatticeRef lr, int alteredState, ref Loc loc,
15131521
DFALatticeRef indexLR = DFALatticeRef.init)
15141522
{
1515-
15161523
DFAVar* assignToCtx = assignTo.getContextVar;
15171524
DFAVar* lrCtx;
15181525
DFAConsequence* lrCctx = lr.getContext(lrCtx);
@@ -1521,6 +1528,11 @@ struct DFAAnalyzer
15211528
const unmodellable = lrCtx !is null && !lrCtx.isModellable;
15221529
DFALatticeRef ret;
15231530

1531+
version(none)
1532+
{
1533+
printf("assigning to var=%p, construct=%d, isBlit=%d, alteredState=%d, noLR=%d, lrIsTruthy=%d, unmodellable=%d\n", assignToCtx, construct, isBlit, alteredState, noLR, lrIsTruthy, unmodellable);
1534+
}
1535+
15241536
this.onRead(assignTo, loc, true);
15251537
// Explicitly allow returns of uninitialized variables.
15261538
this.onRead(lr, loc, assignToCtx !is null && assignToCtx is dfaCommon.getReturnVariable);
@@ -2573,6 +2585,7 @@ struct DFAAnalyzer
25732585
printf("found storage %p, hadAnIndirection=%d, hadAnOuterDeref=%d, takenAddressOf=%d, hadFields=%d, isOffsetOfStorage=%d, unknown=%d\n",
25742586
var, hadAnIndirection, hadAnOuterDeref, takenAddressOf,
25752587
hadFields, isOffsetOfStorage, unknown);
2588+
printf(" isByRef=%d\n", var.isByRef);
25762589
}
25772590

25782591
// Storage consequence may not have a object available for it.
@@ -3190,6 +3203,13 @@ private:
31903203
{
31913204
assignTo.visitIndirectSources((DFAVar* var, bool hadAnIndirection, bool hadAnInnerDeref, bool hadAnOuterDeref,
31923205
bool takenAddressOf, bool hadFields, bool isOffsetOfStorage, ref bool unknown) {
3206+
version (none)
3207+
{
3208+
printf("Indirect source for %p, hadAnIndirection=%d, hadAnInnerDeref=%d, hadAnOuterDeref=%d, takenAddressOf=%d, hadFields=%d, isOffsetOfStorage=%d\n",
3209+
var, hadAnIndirection, hadAnInnerDeref, hadAnOuterDeref,
3210+
takenAddressOf, hadFields, isOffsetOfStorage);
3211+
}
3212+
31933213
if (hadAnIndirection || hadAnInnerDeref)
31943214
return;
31953215

compiler/src/dmd/dfa/fast/expression.d

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -696,10 +696,6 @@ struct ExpressionWalker
696696
}
697697
}
698698

699-
// does this var escape another? Can't model that.
700-
if (dfaVar.isByRef)
701-
markUnmodellable(ei.exp);
702-
703699
DFALatticeRef lr = this.walk(ei.exp);
704700

705701
if (!(ei.exp.isConstructExp || ei.exp.isBlitExp))
@@ -905,19 +901,27 @@ struct ExpressionWalker
905901
// See Slice
906902
auto ie = expr.isIndexExp;
907903

908-
dfaCommon.printStateln("index rhs");
909-
DFALatticeRef index = this.walk(ie.e2);
904+
dfaCommon.printStructureln("Index[key] expression");
905+
910906
dfaCommon.printStateln("index lhs");
911907
DFALatticeRef lhs = this.walk(ie.e1);
908+
lhs.printState("lhs");
909+
910+
dfaCommon.printStateln("index rhs");
911+
DFALatticeRef index = this.walk(ie.e2);
912+
index.printState("rhs");
912913

913914
DFAVar* lhsCtx;
914915
DFAConsequence* lhsCctx = lhs.getContext(lhsCtx);
916+
DFAVar* indexVar = dfaCommon.findIndexVar(lhsCtx);
915917

916918
DFAObject* lhsObject;
917919

918920
Type lhsType = ie.e1.type;
919921
bool resultHasEffect;
920922

923+
DFALatticeRef ret;
924+
921925
if (lhsCctx !is null)
922926
{
923927
// Apply effects that this operation will have on to the lhs
@@ -941,31 +945,39 @@ struct ExpressionWalker
941945
}
942946
}
943947

944-
if (lhsCtx !is null && lhsCtx.isNullable)
948+
if (lhsType.isTypeSArray !is null)
945949
{
946-
// Dereference the lhs if its a pointer,
947-
// this really should be the case, but it prevents unnecessary work for static arrays.
948-
lhs = seeDereference(ie.loc, lhs);
950+
// T[X] lhs;
951+
// lhs[index]
952+
953+
ret = this.seeLogicalAnd(lhs, index);
954+
DFAConsequence* newCctx = ret.setContext(indexVar);
955+
956+
if (lhsObject !is null && lhsObject.onTheStack)
957+
newCctx.obj = dfaCommon.makeInCellObject(lhsObject);
949958
}
959+
else
960+
{
961+
// T[] lhs;
962+
// lhs[index]
950963

951-
DFAVar* indexVar = dfaCommon.findIndexVar(lhsCtx);
952-
DFALatticeRef combined = this.seeLogicalAnd(lhs, index);
964+
if (lhsCtx !is null && lhsCtx.isNullable)
965+
{
966+
// Dereference the lhs if its a pointer,
967+
// this really should be the case, but it prevents unnecessary work for static arrays.
968+
lhs = seeDereference(ie.loc, lhs);
969+
}
953970

954-
DFAConsequence* newCctx = combined.addConsequence(indexVar);
955-
combined.setContext(newCctx);
971+
// (*lhs)[index]
956972

957-
if (lhsObject !is null)
958-
newCctx.obj = dfaCommon.makeInCellObject(lhsObject);
973+
ret = this.seeLogicalAnd(lhs, index);
974+
DFAConsequence* newCctx = ret.setContext(indexVar);
959975

960-
if (resultHasEffect && indexVar !is null)
961-
{
962-
if (indexVar.isTruthy)
963-
newCctx.truthiness = Truthiness.True;
964-
if (indexVar.isNullable)
965-
newCctx.nullable = Nullable.NonNull;
976+
if (lhsObject !is null)
977+
newCctx.obj = dfaCommon.makeObject(lhsObject);
966978
}
967979

968-
return combined;
980+
return ret;
969981
}
970982

971983
case EXP.slice:
@@ -1488,18 +1500,17 @@ struct ExpressionWalker
14881500
{
14891501
stmtWalker.startScope;
14901502
dfaCommon.currentDFAScope.sideEffectFree = true;
1503+
dfaCommon.currentDFAScope.inConditional = true;
14911504

14921505
dfaCommon.printStateln("Question condition:");
14931506
conditionLR = this.walkCondition(qe.econd, predicateNegation);
14941507
conditionVar = conditionLR.getGateConsequenceVariable;
14951508

1496-
stmtWalker.endScope;
1509+
dfaCommon.currentDFAScope.sideEffectFree = false;
14971510
}
14981511

14991512
{
15001513
dfaCommon.printStateln("Question true branch:");
1501-
stmtWalker.startScope;
1502-
dfaCommon.currentDFAScope.inConditional = true;
15031514

15041515
DFAConsequence* c = conditionLR.getContext;
15051516
if (c !is null)

compiler/src/dmd/dfa/fast/report.d

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,13 @@ private:
575575
void inferParameter(DFAVar* from, bool hadAnIndirection,
576576
bool haveConstraintOnLifeTime, bool inCell)
577577
{
578+
version (none)
579+
{
580+
printf("wanting to infer param rel from=%p, hadAnIndirection=%d, haveConstraintOnLifeTime=%d, inCell=%d, intoVar=%p, paramId=%d\n",
581+
from, hadAnIndirection, haveConstraintOnLifeTime,
582+
inCell, intoVar, intoVar.param.parameterId);
583+
}
584+
578585
EscapedRelationship currentRel = from.param.inferred.willEscape(
579586
intoVar.param.parameterId);
580587

@@ -614,7 +621,7 @@ private:
614621

615622
version (none)
616623
{
617-
printf("potential escape 1 obj %p, hadAnIndirection=%d, haveConstraintOnLifeTime=%d, inCell=%d\n",
624+
printf("potential escape 1 obj=%p, hadAnIndirection=%d, haveConstraintOnLifeTime=%d, inCell=%d\n",
618625
obj, hadAnIndirection, haveConstraintOnLifeTime, inCell);
619626
}
620627

compiler/src/dmd/dfa/fast/statement.d

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,9 +647,14 @@ final:
647647
DFAVar* conditionVar;
648648

649649
{
650+
this.startScope;
651+
dfaCommon.currentDFAScope.inConditional = true;
652+
dfaCommon.currentDFAScope.sideEffectFree = true;
653+
650654
conditionLR = expWalker.walkCondition(ifs.condition, predicateNegation);
651655
conditionVar = conditionLR.getGateConsequenceVariable;
652656

657+
dfaCommon.currentDFAScope.sideEffectFree = false;
653658
seeRead(conditionLR, ifs.condition.loc);
654659
}
655660

@@ -678,9 +683,6 @@ final:
678683

679684
{
680685
dfaCommon.printStructureln("If true branch:");
681-
this.startScope;
682-
dfaCommon.currentDFAScope.inConditional = true;
683-
684686
expWalker.seeSilentAssert(trueCondition, true, true);
685687
this.applyGateOnBranch(conditionVar, predicateNegation, true);
686688
}

compiler/src/dmd/func.d

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,7 @@ extern(D) struct ParameterDFAInfo
253253
else if (id == -1)
254254
return cast(EscapedRelationship)((this.escapesInto >> 4) & 0x3);
255255

256-
this.escapesInto >>= 6 + (id * 2);
257-
return cast(EscapedRelationship)(this.escapesInto & 0x3);
256+
return cast(EscapedRelationship)((this.escapesInto >> (6 + (id * 2))) & 0x3);
258257
}
259258

260259
/// Set this parameter's relationship strength to the following parameter id.

compiler/test/compilable/fastdfa.d

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,4 +1415,17 @@ void classOk()
14151415
int** b = c.get(); // ok: heap owner, no lifetime constraint
14161416
}
14171417

1418+
void borrowInConditionNoInfectElse()
1419+
{
1420+
int* x;
1421+
if (borrowFn2(&x) != null)
1422+
{
1423+
// true branch - borrow active
1424+
}
1425+
else
1426+
{
1427+
x = null; // ok - borrow not active in false branch
1428+
}
1429+
}
1430+
14181431
/****************** End borrow checker (ok) ******************/

compiler/test/fail_compilation/fastdfa.d

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,30 @@ fail_compilation/fastdfa.d(1455): For variable `b`
7373
fail_compilation/fastdfa.d(1474): Error: A borrow cannot outlive the variable it borrows from
7474
fail_compilation/fastdfa.d(1473): Possible source `c`
7575
fail_compilation/fastdfa.d(1471): The borrow is stored in variable `b`
76-
fail_compilation/fastdfa.d(1487): Error: Cannot mutate the owner of an active borrow
77-
fail_compilation/fastdfa.d(1482): For variable `s`
78-
fail_compilation/fastdfa.d(1483): Borrowed here
79-
fail_compilation/fastdfa.d(1488): Error: Cannot pass the owner of an active borrow to a function that may mutate it
80-
fail_compilation/fastdfa.d(1488): Parameter `obj` must be const or immutable
81-
fail_compilation/fastdfa.d(1483): Borrowed here
76+
fail_compilation/fastdfa.d(1488): Error: Cannot mutate the owner of an active borrow
77+
fail_compilation/fastdfa.d(1483): For variable `s`
78+
fail_compilation/fastdfa.d(1484): Borrowed here
79+
fail_compilation/fastdfa.d(1489): Error: Cannot pass the owner of an active borrow to a function that may mutate it
80+
fail_compilation/fastdfa.d(1489): Parameter `obj` must be const or immutable
81+
fail_compilation/fastdfa.d(1484): Borrowed here
82+
fail_compilation/fastdfa.d(1497): Error: Cannot mutate the owner of an active borrow
83+
fail_compilation/fastdfa.d(1494): For variable `x`
84+
fail_compilation/fastdfa.d(1495): Borrowed here
85+
fail_compilation/fastdfa.d(1498): Error: Cannot mutate the owner of an active borrow
86+
fail_compilation/fastdfa.d(1494): For variable `x`
87+
fail_compilation/fastdfa.d(1495): Borrowed here
88+
fail_compilation/fastdfa.d(1499): Error: Cannot mutate the owner of an active borrow
89+
fail_compilation/fastdfa.d(1494): For variable `x`
90+
fail_compilation/fastdfa.d(1496): Borrowed here
91+
fail_compilation/fastdfa.d(1509): Error: Cannot mutate the owner of an active borrow
92+
fail_compilation/fastdfa.d(1504): For variable `x`
93+
fail_compilation/fastdfa.d(1506): Borrowed here
94+
fail_compilation/fastdfa.d(1517): Error: Cannot mutate the owner of an active borrow
95+
fail_compilation/fastdfa.d(1513): For variable `x`
96+
fail_compilation/fastdfa.d(1516): Borrowed here
97+
fail_compilation/fastdfa.d(1526): Error: Cannot mutate the owner of an active borrow
98+
fail_compilation/fastdfa.d(1523): For variable `x`
99+
fail_compilation/fastdfa.d(1524): Borrowed here
82100
---
83101
*/
84102

@@ -516,8 +534,8 @@ void methodOutliveErr()
516534
void methodPassErr()
517535
{
518536
BorrowStruct s;
519-
int** b = s.get(); // error: passing the borrowed owner to a mutating function
520-
methodTake(&s.p);
537+
int** b = s.get();
538+
methodTake(&s.p); // error: passing the borrowed owner to a mutating function
521539
}
522540

523541
void borrowOutliveErr1()
@@ -561,7 +579,8 @@ void classOutliveErr()
561579
}
562580
}
563581

564-
void borrowMutateAssignCall() {
582+
void borrowMutateAssignCall()
583+
{
565584
void call(ref const BorrowStruct, scope int**) {}
566585
void borrow(scope int**) {}
567586

@@ -574,4 +593,46 @@ void borrowMutateAssignCall() {
574593
destroy(s); // error
575594
}
576595

596+
void ternaryByRefNoInfect(bool condition)
597+
{
598+
int* x;
599+
int** q = condition ? borrowFn2(&x) : &x;
600+
int** r = condition ?
601+
borrowFn2(&x) : // error borrow could be in here, and param is not const
602+
borrowFn2(&x); // error
603+
x = null;
604+
}
605+
606+
void borrowInTernaryConditionNoInfect()
607+
{
608+
int* x;
609+
int* y;
610+
int** result = (borrowFn2(&x) != null) ?
611+
borrowFn2(&x) : // ok, the previous borrow wasn't stored
612+
&y;
613+
x = null; // error could be a borrow
614+
}
615+
616+
void borrowInTernaryConditionInfect() {
617+
int* x;
618+
int* y;
619+
int** temp;
620+
int** result = ((temp = borrowFn2(&x)) !is null) ?
621+
borrowFn2(&x) : // error
622+
&y;
623+
}
624+
625+
void borrowInConditionInfect()
626+
{
627+
int* x = new int;
628+
if (int** temp = borrowFn2(&x))
629+
{
630+
x = null; // error
631+
}
632+
else
633+
{
634+
x = null; // ok - borrow not active in false branch
635+
}
636+
}
637+
577638
/****************** End borrow checker (errors) ******************/

0 commit comments

Comments
 (0)