Skip to content

DAA and 'out' parameters #20506

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ type
nkPtrTy, # ``ptr T``
nkVarTy, # ``var T``
nkConstTy, # ``const T``
nkMutableTy, # ``mutable T``
nkOutTy, # ``out T``
nkDistinctTy, # distinct type
nkProcTy, # proc type
nkIteratorTy, # iterator type
Expand Down Expand Up @@ -513,7 +513,7 @@ type
nfUseDefaultField # node has a default value (object constructor)

TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 45)
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 46)
tfVarargs, # procedure has C styled varargs
# tyArray type represeting a varargs list
tfNoSideEffect, # procedure type does not allow side effects
Expand Down Expand Up @@ -582,6 +582,7 @@ type
tfExplicitCallConv
tfIsConstructor
tfEffectSystemWorkaround
tfIsOutParam

TTypeFlags* = set[TTypeFlag]

Expand Down Expand Up @@ -632,7 +633,7 @@ const
skError* = skUnknown

var
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect}
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam}
## type flags that are essential for type equality.
## This is now a variable because for emulation of version:1.0 we
## might exclude {tfGcSafe, tfNoSideEffect}.
Expand Down Expand Up @@ -2129,6 +2130,8 @@ proc isNewStyleConcept*(n: PNode): bool {.inline.} =
assert n.kind == nkTypeClassTy
result = n[0].kind == nkEmpty

proc isOutParam*(t: PType): bool {.inline.} = tfIsOutParam in t.flags

const
nodesToIgnoreSet* = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit,
nkTypeSection, nkProcDef, nkConverterDef,
Expand Down
2 changes: 2 additions & 0 deletions compiler/condsyms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,5 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasCstringCase")
defineSymbol("nimHasCallsitePragma")
defineSymbol("nimHasAmbiguousEnumHint")

defineSymbol("nimHasOutParams")
7 changes: 3 additions & 4 deletions compiler/dfa.nim
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,9 @@ proc genCall(c: var Con; n: PNode) =
if t != nil: t = t.skipTypes(abstractInst)
for i in 1..<n.len:
gen(c, n[i])
when false:
if t != nil and i < t.len and t[i].kind == tyOut:
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
if t != nil and i < t.len and isOutParam(t[i]):
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
# every call can potentially raise:
if false: # c.inTryStmt > 0 and canRaiseConservative(n[0]):
# we generate the instruction sequence:
Expand Down
4 changes: 2 additions & 2 deletions compiler/jsgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
var
x, y: TCompRes
xLoc,yLoc: Rope
xLoc, yLoc: Rope
let i = ord(optOverflowCheck notin p.options)
useMagic(p, jsMagics[op][i])
if n.len > 2:
Expand All @@ -614,7 +614,7 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
template applyFormat(frmtA, frmtB) =
if i == 0: applyFormat(frmtA) else: applyFormat(frmtB)

case op:
case op
of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)")
of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)")
of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)")
Expand Down
2 changes: 1 addition & 1 deletion compiler/lineinfos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ type

proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv}
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
result[1] = result[2] - {warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
Expand Down
2 changes: 1 addition & 1 deletion compiler/nim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
case conf.cmd
of cmdBackends, cmdTcc:
let nimRunExe = getNimRunExe(conf)
var cmdPrefix: string
var cmdPrefix = ""
if nimRunExe.len > 0: cmdPrefix.add nimRunExe.quoteShell
case conf.backend
of backendC, backendCpp, backendObjc: discard
Expand Down
3 changes: 2 additions & 1 deletion compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ type
overloadableEnums, # deadcode
strictEffects,
unicodeOperators, # deadcode
flexibleOptionalParams
flexibleOptionalParams,
strictDefs

LegacyFeature* = enum
allowSemcheckedAstModification,
Expand Down
5 changes: 1 addition & 4 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1323,10 +1323,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
optInd(p, result)
result.add(primary(p, pmNormal))
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
of tkOut:
# I like this parser extension to be in 1.4 as it still might turn out
# useful in the long run.
result = parseTypeDescKAux(p, nkMutableTy, mode)
of tkOut: result = parseTypeDescKAux(p, nkOutTy, mode)
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
Expand Down
12 changes: 9 additions & 3 deletions compiler/renderer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
of nkVarTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var")
of nkVarTy, nkOutTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var")
of nkDistinctTy:
result = len("distinct") + (if n.len > 0: lsub(g, n[0])+1 else: 0)
if n.len > 1:
Expand Down Expand Up @@ -607,8 +607,8 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
let inPragma = g.inPragma == 1 # just the top-level
var inHideable = false
for i in start..n.len + theEnd:
var c = i < n.len + theEnd
var sublen = lsub(g, n[i]) + ord(c)
let c = i < n.len + theEnd
let sublen = lsub(g, n[i]) + ord(c)
if not fits(g, g.lineLen + sublen) and (ind + sublen < MaxLineLen): optNL(g, ind)
let oldLen = g.tokens.len
if inPragma:
Expand Down Expand Up @@ -1384,6 +1384,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
gsub(g, n[0])
else:
put(g, tkVar, "var")
of nkOutTy:
if n.len > 0:
putWithSpace(g, tkOut, "out")
gsub(g, n[0])
else:
put(g, tkOut, "out")
of nkDistinctTy:
if n.len > 0:
putWithSpace(g, tkDistinct, "distinct")
Expand Down
38 changes: 22 additions & 16 deletions compiler/sempass2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,8 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
tracked.owner.flags.incl sfInjectDestructors

proc isLocalVar(a: PEffects, s: PSym): bool =
# and (s.kind != skParam or s.typ.kind == tyOut)
s.kind in {skVar, skResult} and sfGlobal notin s.flags and
s.owner == a.owner and s.typ != nil
s.typ != nil and (s.kind in {skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
sfGlobal notin s.flags and s.owner == a.owner

proc getLockLevel(t: PType): TLockLevel =
var t = t
Expand Down Expand Up @@ -194,7 +193,11 @@ proc varDecl(a: PEffects; n: PNode) {.inline.} =
if n.kind == nkSym:
a.scopes[n.sym.id] = a.currentBlock

proc skipHiddenDeref(n: PNode): PNode {.inline.} =
result = if n.kind == nkHiddenDeref: n[0] else: n

proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
let n = skipHiddenDeref(n)
if n.kind != nkSym: return
let s = n.sym
if isLocalVar(a, s):
Expand All @@ -221,6 +224,7 @@ proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
n.flags.incl nfFirstWrite

proc initVarViaNew(a: PEffects, n: PNode) =
let n = skipHiddenDeref(n)
if n.kind != nkSym: return
let s = n.sym
if {tfRequiresInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
Expand Down Expand Up @@ -348,7 +352,8 @@ proc useVar(a: PEffects, n: PNode) =
if s.typ.requiresInit:
message(a.config, n.info, warnProveInit, s.name.s)
elif a.leftPartOfAsgn <= 0:
message(a.config, n.info, warnUninit, s.name.s)
if strictDefs in a.c.features:
message(a.config, n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable:
a.init.add s.id
useVarNoInitCheck(a, n, s)
Expand Down Expand Up @@ -945,17 +950,18 @@ proc trackCall(tracked: PEffects; n: PNode) =

if op != nil and op.kind == tyProc:
for i in 1..<min(n.safeLen, op.len):
case op[i].kind
let paramType = op[i]
case paramType.kind
of tySink:
createTypeBoundOps(tracked, op[i][0], n.info)
createTypeBoundOps(tracked, paramType[0], n.info)
checkForSink(tracked, n[i])
of tyVar:
tracked.hasDangerousAssign = true
#of tyOut:
# consider this case: p(out x, x); we want to remark that 'x' is not
# initialized until after the call. Since we do this after we analysed the
# call, this is fine.
# initVar(tracked, n[i].skipAddr, false)
if isOutParam(paramType):
# consider this case: p(out x, x); we want to remark that 'x' is not
# initialized until after the call. Since we do this after we analysed the
# call, this is fine.
initVar(tracked, n[i].skipAddr, false)
else: discard

type
Expand Down Expand Up @@ -1504,13 +1510,13 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
(t.config.selectedGC in {gcArc, gcOrc} and
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
createTypeBoundOps(t, typ, param.info)
when false:
if typ.kind == tyOut and param.id notin t.init:
message(g.config, param.info, warnProveInit, param.name.s)
if isOutParam(typ) and param.id notin t.init:
message(g.config, param.info, warnProveInit, param.name.s)

if not isEmptyType(s.typ[0]) and
(s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar) and
s.kind in {skProc, skFunc, skConverter, skMethod}:
(s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar or
strictDefs in c.features) and
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
var res = s.ast[resultPos].sym # get result symbol
if res.id notin t.init:
message(g.config, body.info, warnProveInit, "result")
Expand Down
10 changes: 6 additions & 4 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,10 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
addSonSkipIntLit(result, errorType(c), c.idgen)

proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
proc semVarOutType(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
if n.len == 1:
result = newOrPrevType(kind, prev, c)
result = newOrPrevType(tyVar, prev, c)
result.flags = flags
var base = semTypeNode(c, n[0], nil)
if base.kind == tyTypeDesc and not isSelf(base):
base = base[0]
Expand All @@ -206,7 +207,7 @@ proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
base = base[0]
addSonSkipIntLit(result, base, c.idgen)
else:
result = newConstraint(c, kind)
result = newConstraint(c, tyVar)

proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
if t == nil:
Expand Down Expand Up @@ -2015,7 +2016,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
of nkVarTy: result = semVarOutType(c, n, prev, tyVar)
of nkVarTy: result = semVarOutType(c, n, prev, {})
of nkOutTy: result = semVarOutType(c, n, prev, {tfIsOutParam})
of nkDistinctTy: result = semDistinct(c, n, prev)
of nkStaticTy: result = semStaticType(c, n[0], prev)
of nkIteratorTy:
Expand Down
27 changes: 19 additions & 8 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ type
trDontBind
trNoCovariance
trBindGenericParam # bind tyGenericParam even with trDontBind
trIsOutParam

TTypeRelFlags* = set[TTypeRelFlag]

Expand Down Expand Up @@ -545,8 +546,9 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} =
result = if tfNotNil notin f.flags: isSubtype else: isNone

proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and
(f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})
result = (f.kind != a.kind and
(f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})) or
isOutParam(f) != isOutParam(a)

proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
## For example we have:
Expand Down Expand Up @@ -1162,9 +1164,18 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
of tyFloat32: result = handleFloatRange(f, a)
of tyFloat64: result = handleFloatRange(f, a)
of tyFloat128: result = handleFloatRange(f, a)
of tyVar, tyLent:
if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base, flags)
else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
of tyVar:
let flags = if isOutParam(f): flags + {trIsOutParam} else: flags
if aOrig.kind == f.kind and (isOutParam(aOrig) == isOutParam(f)):
result = typeRel(c, f.base, aOrig.base, flags)
else:
result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
subtypeCheck()
of tyLent:
if aOrig.kind == f.kind:
result = typeRel(c, f.base, aOrig.base, flags)
else:
result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
subtypeCheck()
of tyArray:
case a.kind
Expand Down Expand Up @@ -1293,7 +1304,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
if sameObjectTypes(f, a):
result = isEqual
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
else:
elif trIsOutParam notin flags:
var depth = isObjectSubtype(c, a, f, nil)
if depth > 0:
inc(c.inheritancePenalty, depth)
Expand Down Expand Up @@ -1461,7 +1472,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
elif aAsObject.kind == fKind:
aAsObject = aAsObject.base

if aAsObject.kind == tyObject:
if aAsObject.kind == tyObject and trIsOutParam notin flags:
let baseType = aAsObject.base
if baseType != nil:
c.inheritancePenalty += 1
Expand Down Expand Up @@ -1637,7 +1648,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
elif a.len > 0 and a.lastSon == f:
# Needed for checking `Y` == `Addable` in the following
#[
type
type
Addable = concept a, type A
a + a is A
MyType[T: Addable; Y: static T] = object
Expand Down
4 changes: 2 additions & 2 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
elif t.len == 1: result.add(",")
result.add(')')
of tyPtr, tyRef, tyVar, tyLent:
result = typeToStr[t.kind]
result = if isOutParam(t): "out " else: typeToStr[t.kind]
if t.len >= 2:
setLen(result, result.len-1)
result.add '['
Expand Down Expand Up @@ -1211,7 +1211,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameChildrenAux(a, b, c)
if result:
if IgnoreTupleFields in c.flags:
result = a.flags * {tfVarIsPtr} == b.flags * {tfVarIsPtr}
result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
else:
result = sameFlags(a, b)
if result and ExactGcSafety in c.flags:
Expand Down
Loading