From ab26298a035ed794fc73724a6ae06eefd6c764d3 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Aug 2015 17:31:00 +0200 Subject: added system.unsafeAddr --- compiler/parampatterns.nim | 20 +++++++++++--------- compiler/semexprs.nim | 6 +++--- compiler/semmagic.nim | 6 +++--- lib/system.nim | 7 +++++++ tests/ccgbugs/tunsafeaddr.nim | 19 +++++++++++++++++++ todo.txt | 1 - web/news.txt | 5 +++++ 7 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 tests/ccgbugs/tunsafeaddr.nim diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index b7fe269df..ae391945a 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -178,13 +178,14 @@ type arDiscriminant, # is a discriminant arStrange # it is a strange beast like 'typedesc[var T]' -proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = +proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult = ## 'owner' can be nil! result = arNone case n.kind of nkSym: - # don't list 'skLet' here: - if n.sym.kind in {skVar, skResult, skTemp}: + let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet} + else: {skVar, skResult, skTemp} + if n.sym.kind in kinds: if owner != nil and owner.id == n.sym.owner.id and sfGlobal notin n.sym.flags: result = arLocalLValue @@ -200,7 +201,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = {tyVar, tyPtr, tyRef}: result = arLValue else: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) if result != arNone and sfDiscriminant in n.sons[1].sym.flags: result = arDiscriminant of nkBracketExpr: @@ -208,23 +209,24 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = {tyVar, tyPtr, tyRef}: result = arLValue else: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) of nkHiddenStdConv, nkHiddenSubConv, nkConv: # Object and tuple conversions are still addressable, so we skip them # XXX why is 'tyOpenArray' allowed here? if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in {tyOpenArray, tyTuple, tyObject}: - result = isAssignable(owner, n.sons[1]) + result = isAssignable(owner, n.sons[1], isUnsafeAddr) elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct): # types that are equal modulo distinction preserve l-value: - result = isAssignable(owner, n.sons[1]) + result = isAssignable(owner, n.sons[1], isUnsafeAddr) of nkHiddenDeref, nkDerefExpr, nkHiddenAddr: result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) of nkCallKinds: # builtin slice keeps lvalue-ness: - if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1]) + if getMagic(n) == mSlice: + result = isAssignable(owner, n.sons[1], isUnsafeAddr) else: discard diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fba64776d..b4308def3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -597,8 +597,8 @@ proc skipObjConv(n: PNode): PNode = of nkObjUpConv, nkObjDownConv: result = n.sons[0] else: result = n -proc isAssignable(c: PContext, n: PNode): TAssignableResult = - result = parampatterns.isAssignable(c.p.owner, n) +proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = + result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or @@ -1700,7 +1700,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.magic # magics that need special treatment of mAddr: checkSonsLen(n, 2) - result = semAddr(c, n.sons[1]) + result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr") of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 0a7846f1d..0afbf1f07 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -10,10 +10,10 @@ # This include file implements the semantic checking for magics. # included from sem.nim -proc semAddr(c: PContext; n: PNode): PNode = +proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode = result = newNodeI(nkAddr, n.info) let x = semExprWithType(c, n) - if isAssignable(c, x) notin {arLValue, arLocalLValue}: + if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: localError(n.info, errExprHasNoAddress) result.add x result.typ = makePtrType(c, x.typ) @@ -119,7 +119,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, case n[0].sym.magic of mAddr: checkSonsLen(n, 2) - result = semAddr(c, n.sons[1]) + result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr") of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) diff --git a/lib/system.nim b/lib/system.nim index 93d76e78b..e5cae1336 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -153,6 +153,13 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard +proc unsafeAddr*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = + ## Builtin 'addr' operator for taking the address of a memory location. + ## This works even for ``let`` variables or parameters for better interop + ## with C and so it is considered even more unsafe than the ordinary ``addr``. + ## Cannot be overloaded. + discard + proc `type`*(x: expr): typeDesc {.magic: "TypeOf", noSideEffect.} = ## Builtin 'type' operator for accessing the type of an expression. ## Cannot be overloaded. diff --git a/tests/ccgbugs/tunsafeaddr.nim b/tests/ccgbugs/tunsafeaddr.nim new file mode 100644 index 000000000..4f05c7c21 --- /dev/null +++ b/tests/ccgbugs/tunsafeaddr.nim @@ -0,0 +1,19 @@ +discard """ + output: '''12''' +""" + +{.emit: """ +long sum(long* a, long len) { + long i, result = 0; + for (i = 0; i < len; ++i) result += a[i]; + return result; +} +""".} + +proc sum(a: ptr int; len: int): int {.importc, nodecl.} + +proc main = + let foo = [8, 3, 1] + echo sum(unsafeAddr foo[0], foo.len) + +main() diff --git a/todo.txt b/todo.txt index 8734b8f19..2955f2917 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.11.4 ============== -- ``unsafeAddr`` - document special cased varargs[untyped] and varargs[typed] - The remaining bugs of the lambda lifting pass that is responsible to enable diff --git a/web/news.txt b/web/news.txt index b72ae56d2..518793a4a 100644 --- a/web/news.txt +++ b/web/news.txt @@ -57,6 +57,11 @@ News Language Additions ------------------ + - ``system.unsafeAddr`` can be used to access the address of a ``let`` + variable or parameter for C interoperability. Since technically this + makes parameters and ``let`` variables mutable, it is considered even more + unsafe than the ordinary ``addr`` builtin. + Bugfixes -------- -- cgit 1.4.1-2-gfad0