This commit is contained in:
Andreas Rumpf
2021-09-03 21:52:24 +02:00
committed by GitHub
parent c2b20516d3
commit cddf8ec6f6
9 changed files with 103 additions and 20 deletions

View File

@@ -160,6 +160,7 @@ type
importModuleMap*: Table[int, int] # (module.id, module.id)
lastTLineInfo*: TLineInfo
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
inUncheckedAssignSection*: int
template config*(c: PContext): ConfigRef = c.graph.config

View File

@@ -691,8 +691,12 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
else:
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
result.add n
if isAssignable(c, n) notin {arLValue, arLocalLValue}:
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
let aa = isAssignable(c, n)
if aa notin {arLValue, arLocalLValue}:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
else:
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
result = n
@@ -738,9 +742,13 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
if i < t.len and t[i] != nil and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
let it = n[i]
if isAssignable(c, it) notin {arLValue, arLocalLValue}:
let aa = isAssignable(c, it)
if aa notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
localError(c.config, it.info, errVarForOutParamNeededX % $it)
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
else:
localError(c.config, it.info, errVarForOutParamNeededX % $it)
# bug #5113: disallow newSeq(result) where result is a 'var T':
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
var arg = n[1] #.skipAddr

View File

@@ -200,20 +200,22 @@ proc semConstructFields(c: PContext, n: PNode,
if selectedBranch != -1:
template badDiscriminatorError =
let fields = fieldsPresentInBranch(selectedBranch)
localError(c.config, constrCtx.initExpr.info,
("cannot prove that it's safe to initialize $1 with " &
"the runtime value for the discriminator '$2' ") %
[fields, discriminator.sym.name.s])
if c.inUncheckedAssignSection == 0:
let fields = fieldsPresentInBranch(selectedBranch)
localError(c.config, constrCtx.initExpr.info,
("cannot prove that it's safe to initialize $1 with " &
"the runtime value for the discriminator '$2' ") %
[fields, discriminator.sym.name.s])
mergeInitStatus(result, initNone)
template wrongBranchError(i) =
let fields = fieldsPresentInBranch(i)
localError(c.config, constrCtx.initExpr.info,
"a case selecting discriminator '$1' with value '$2' " &
"appears in the object construction, but the field(s) $3 " &
"are in conflict with this value." %
[discriminator.sym.name.s, discriminatorVal.renderTree, fields])
if c.inUncheckedAssignSection == 0:
let fields = fieldsPresentInBranch(i)
localError(c.config, constrCtx.initExpr.info,
("a case selecting discriminator '$1' with value '$2' " &
"appears in the object construction, but the field(s) $3 " &
"are in conflict with this value.") %
[discriminator.sym.name.s, discriminatorVal.renderTree, fields])
template valuesInConflictError(valsDiff) =
localError(c.config, discriminatorVal.info, ("possible values " &
@@ -251,9 +253,10 @@ proc semConstructFields(c: PContext, n: PNode,
badDiscriminatorError()
elif discriminatorVal.sym.kind notin {skLet, skParam} or
discriminatorVal.sym.typ.kind in {tyVar}:
localError(c.config, discriminatorVal.info,
"runtime discriminator must be immutable if branch fields are " &
"initialized, a 'let' binding is required.")
if c.inUncheckedAssignSection == 0:
localError(c.config, discriminatorVal.info,
"runtime discriminator must be immutable if branch fields are " &
"initialized, a 'let' binding is required.")
elif ctorCase[ctorIdx].kind == nkElifBranch:
localError(c.config, discriminatorVal.info, "branch initialization " &
"with a runtime discriminator is not supported inside of an " &

View File

@@ -961,6 +961,8 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
else:
bc.exc = newNodeI(nkArgList, pragma.info)
bc.exc.add n
of wUncheckedAssign:
discard "handled in sempass1"
else:
localError(tracked.config, pragma.info,
"invalid pragma block: " & $pragma)

View File

@@ -2223,7 +2223,21 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
checkSonsLen(n, 2, c.config)
let pragmaList = n[0]
pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
var inUncheckedAssignSection = 0
for p in pragmaList:
if whichPragma(p) == wCast:
case whichPragma(p[1])
of wGcSafe, wNoSideEffect, wTags, wRaises:
discard "handled in sempass2"
of wUncheckedAssign:
inUncheckedAssignSection = 1
else:
localError(c.config, p.info, "invalid pragma block: " & $p)
inc c.inUncheckedAssignSection, inUncheckedAssignSection
n[1] = semExpr(c, n[1])
dec c.inUncheckedAssignSection, inUncheckedAssignSection
result = n
result.typ = n[1].typ
for i in 0..<pragmaList.len:

View File

@@ -1879,6 +1879,16 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
result.add c.graph.emptyNode
result.add arg
proc isLValue(c: PContext; n: PNode): bool {.inline.} =
let aa = isAssignable(nil, n)
case aa
of arLValue, arLocalLValue, arStrange:
result = true
of arDiscriminant:
result = c.inUncheckedAssignSection > 0
else:
result = false
proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode =
result = nil
@@ -1895,7 +1905,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
let constraint = c.converters[i].typ.n[1].sym.constraint
if not constraint.isNil and not matchNodeKinds(constraint, arg):
continue
if src.kind in {tyVar, tyLent} and not arg.isLValue:
if src.kind in {tyVar, tyLent} and not isLValue(c, arg):
continue
let destIsGeneric = containsGenericType(dest)
@@ -2338,7 +2348,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
if argConverter.typ.kind notin {tyVar}:
m.firstMismatch.kind = kVarNeeded
noMatch()
elif not n.isLValue:
elif not isLValue(c, n):
m.firstMismatch.kind = kVarNeeded
noMatch()

View File

@@ -36,6 +36,7 @@ type
wMemTracker = "memtracker", wObjChecks = "objchecks",
wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine",
wCursor = "cursor", wNoalias = "noalias", wEffectsOf = "effectsOf",
wUncheckedAssign = "uncheckedAssign",
wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor",
wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp",

View File

@@ -1854,6 +1854,41 @@ A small example:
let unknownKindBounded = range[nkAdd..nkSub](unknownKind)
z = Node(kind: unknownKindBounded, leftOp: Node(), rightOp: Node())
cast uncheckedAssign
--------------------
Some restrictions for case objects can be disabled via a `{.cast(unsafeAssign).}` section:
.. code-block:: nim
:test: "nim c $1"
type
TokenKind* = enum
strLit, intLit
Token = object
case kind*: TokenKind
of strLit:
s*: string
of intLit:
i*: int64
proc passToVar(x: var TokenKind) = discard
var t = Token(kind: strLit, s: "abc")
{.cast(uncheckedAssign).}:
# inside the 'cast' section it is allowed to pass 't.kind' to a 'var T' parameter:
passToVar(t.kind)
# inside the 'cast' section it is allowed to set field 's' even though the
# constructed 'kind' field has an unknown value:
t = Token(kind: t.kind, s: "abc")
# inside the 'cast' section it is allowed to assign to the 't.kind' field directly:
t.kind = intLit
Set type
--------

View File

@@ -25,3 +25,12 @@ t.curr = TokenObject(kind: Token.bar, bar: BasicNumber(value: 12.34))
t.curr = TokenObject(kind: Token.foo, foo: "foo")
echo "SUCCESS"
proc passToVar(x: var Token) = discard
{.cast(uncheckedAssign).}:
passToVar(t.curr.kind)
t.curr = TokenObject(kind: t.curr.kind, foo: "abc")
t.curr.kind = Token.foo