Skip to content

Commit

Permalink
Add {.alias.} pragma
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn committed Sep 6, 2022
1 parent 5ebd124 commit 8383949
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 16 deletions.
16 changes: 16 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,22 @@
```nim
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
- Alias-style templates and macros can now optionally be annotated with the
`{.alias.}` pragma. For templates, this has the behavior of disallowing
redefinitions.
```nim
type Foo = object
bar: int
var foo = Foo(bar: 10)
template bar: untyped {.alias.} = foo.bar
assert bar == 10
bar = 15
assert bar == 15
var foo2 = Foo(bar: -10)
# error:
template bar: untyped {.alias.} = foo.bar
```

- `cstring` is now accepted as a selector in `case` statements, removing the
Expand Down
3 changes: 2 additions & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ type
TNodeKinds* = set[TNodeKind]

type
TSymFlag* = enum # 48 flags!
TSymFlag* = enum # 49 flags!
sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed
Expand Down Expand Up @@ -304,6 +304,7 @@ type
sfSingleUsedTemp # For temporaries that we know will only be used once
sfNoalias # 'noalias' annotation, means C's 'restrict'
sfEffectsDelayed # an 'effectsDelayed' parameter
sfAlias # an alias-style template or macro

TSymFlags* = set[TSymFlag]

Expand Down
1 change: 1 addition & 0 deletions compiler/condsyms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasTopDownInference")
defineSymbol("nimHasTemplateRedefinitionPragma")
defineSymbol("nimHasCstringCase")
defineSymbol("nimHasAliasPragma")
6 changes: 4 additions & 2 deletions compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ const
converterPragmas* = procPragmas
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
wDelegator, wExportNims, wUsed, wPragma, wRedefine}
wDelegator, wExportNims, wUsed, wPragma, wRedefine, wAlias}
macroPragmas* = declPragmas + {FirstCallConv..LastCallConv,
wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore,
wDiscardable, wGensym, wInject, wDelegator}
wDiscardable, wGensym, wInject, wDelegator, wAlias}
iteratorPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect,
wMagic, wBorrow,
wDiscardable, wGensym, wInject, wRaises, wEffectsOf,
Expand Down Expand Up @@ -1243,6 +1243,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
pragmaEnsures(c, it)
of wEnforceNoRaises:
sym.flags.incl sfNeverRaises
of wAlias:
sym.flags.incl sfAlias
else: invalidPragma(c, it)
elif comesFromPush and whichKeyword(ident) != wInvalid:
discard "ignore the .push pragma; it doesn't apply"
Expand Down
7 changes: 5 additions & 2 deletions compiler/semtempl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
addInterfaceOverloadableSymAt(c, c.currentScope, s)
elif not comesFromShadowscope:
if {sfTemplateRedefinition, sfGenSym} * s.flags == {}:
#wrongRedefinition(c, n.info, proto.name.s, proto.info)
message(c.config, n.info, warnTemplateRedefinition, s.name.s)
if sfAlias in (proto.flags + s.flags):
# cannot implicitly redefine alias templates
wrongRedefinition(c, n.info, proto.name.s, proto.info)
else:
message(c.config, n.info, warnTemplateRedefinition, s.name.s)
symTabReplace(c.currentScope.symbols, proto, s)
if n[patternPos].kind != nkEmpty:
c.patterns.add(s)
Expand Down
2 changes: 1 addition & 1 deletion compiler/wordrecg.nim
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ type
wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked",
wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain",
wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises",
wRedefine = "redefine",
wRedefine = "redefine", wAlias = "alias",

wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char",
wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default",
Expand Down
26 changes: 21 additions & 5 deletions doc/manual_experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,8 @@ Assuming `foo` is a macro or a template, this is roughly equivalent to:
```


Symbols as template/macro calls
===============================
Alias-style templates and macros
================================

Templates and macros that take no arguments can be called as lone symbols,
i.e. without parentheses. This is useful for repeated uses of complex
Expand All @@ -530,9 +530,25 @@ expressions that cannot conveniently be represented as runtime values.
assert bar == 15
```

In the future, this may require more specific information on template or macro
signatures to be used. Specializations for some applications of this may also
be introduced to guarantee consistency and circumvent bugs.
These templates/macros can be annotated with the `{.alias.}` pragma
to denote their intended use, however this annotation is currently
not required. For templates, this induces the behavior of
disallowing redefinitions.

.. code-block:: nim
type Foo = object
bar: int

var foo = Foo(bar: 10)
template bar: untyped {.alias.} = foo.bar
assert bar == 10
bar = 15
assert bar == 15
var foo2 = Foo(bar: -10)
# error:
template bar: untyped {.alias.} = foo.bar

In the future, this annotation or the lack of it may gain more meanings.


Not nil annotation
Expand Down
11 changes: 8 additions & 3 deletions lib/std/decls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ macro byaddr*(sect) =
typ = def[1]
ex = def[2]
addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ)
result = quote do:
let tmp: `addrTyp` = addr(`ex`)
template `lhs`: untyped = tmp[]
when defined(nimHasAliasPragma):
result = quote do:
let tmp: `addrTyp` = addr(`ex`)
template `lhs`: untyped {.alias.} = tmp[]
else:
result = quote do:
let tmp: `addrTyp` = addr(`ex`)
template `lhs`: untyped = tmp[]
result.copyLineInfo(def)
4 changes: 2 additions & 2 deletions tests/stdlib/tdecls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ template fun() =
var b {.byaddr.}: int = s[0]
doAssert a.addr == b.addr

when false:
# template specific redeclaration issue
when defined(nimHasAliasPragma):
# use of {.alias.} pragma disallows redefinitions
# see https://github.com/nim-lang/Nim/issues/8275
doAssert not compiles(block:
# redeclaration not allowed
Expand Down

0 comments on commit 8383949

Please sign in to comment.