Make {.requiresInit.} to work for distinct types (#15869)

Make `requiresInit` pragma to work for distinct types in addition to
objects. Tagging of distinct types with `requiresInit` pragma was
already supported, but its impact wasn't applied. Now its behavior when
applied on distinct types is as follows.

Given the following distinct type definitions:

  ```nim
  type
    DistinctObject {.requiresInit, borrow: `.`.} = distinct MyObject
    DistinctString {.requiresInit.} = distinct string
  ```

The following code blocks will fail to compile:

  ```nim
  var foo: DistinctFoo
  foo.x = "test"
  doAssert foo.x == "test"
  ```

  ```nim
  var s: DistinctString
  s = "test"
  doAssert s == "test"
  ```

But these ones will compile successfully:

  ```nim
  let foo = DistinctFoo(Foo(x: "test"))
  doAssert foo.x == "test"
  ```

  ```nim
  let s = "test"
  doAssert s == "test"
  ```
This commit is contained in:
Ivan Bobev 2020-11-06 20:56:09 +02:00 • committed by GitHub
commit 3c85aa9e53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 9 deletions

View file

@ -4,6 +4,8 @@
## Standard library additions and changes ## Standard library additions and changes
- Make `{.requiresInit.}` pragma to work for `distinct` types.
- `prelude` now works with the JavaScript target. - `prelude` now works with the JavaScript target.
- Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. - Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`.

View file

@ -355,15 +355,22 @@ proc computeRequiresInit(c: PContext, t: PType): bool =
proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
var objType = t var objType = t
while objType.kind != tyObject: while objType.kind notin {tyObject, tyDistinct}:
objType = objType.lastSon objType = objType.lastSon
assert objType != nil assert objType != nil
if objType.kind == tyObject:
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
let initResult = semConstructTypeAux(c, constrCtx, {}) let initResult = semConstructTypeAux(c, constrCtx, {})
assert constrCtx.missingFields.len > 0 assert constrCtx.missingFields.len > 0
localError(c.config, info, localError(c.config, info,
"The $1 type doesn't have a default value. The following fields must be initialized: $2.", "The $1 type doesn't have a default value. The following fields must " &
"be initialized: $2.",
[typeToString(t), listSymbolNames(constrCtx.missingFields)]) [typeToString(t), listSymbolNames(constrCtx.missingFields)])
elif objType.kind == tyDistinct:
localError(c.config, info,
"The $1 distinct type doesn't have a default value.", [typeToString(t)])
else:
assert false, "Must not enter here."
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var t = semTypeNode(c, n[0], nil) var t = semTypeNode(c, n[0], nil)

View file

@ -611,7 +611,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if def.kind == nkEmpty: if def.kind == nkEmpty:
let actualType = v.typ.skipTypes({tyGenericInst, tyAlias, let actualType = v.typ.skipTypes({tyGenericInst, tyAlias,
tyUserTypeClassInst}) tyUserTypeClassInst})
if actualType.kind == tyObject and actualType.requiresInit: if actualType.kind in {tyObject, tyDistinct} and
actualType.requiresInit:
defaultConstructionError(c, v.typ, v.info) defaultConstructionError(c, v.typ, v.info)
else: else:
checkNilable(c, v) checkNilable(c, v)

View file

@ -260,4 +260,5 @@ proc directViewType*(t: PType): ViewTypeKind =
result = noView result = noView
proc requiresInit*(t: PType): bool = proc requiresInit*(t: PType): bool =
(t.flags * {tfRequiresInit, tfNotNil} != {}) or classifyViewType(t) != noView (t.flags * {tfRequiresInit, tfNeedsFullInit, tfNotNil} != {}) or
classifyViewType(t) != noView

View file

@ -2697,6 +2697,36 @@ the variable has been initialized and does not rely on syntactic properties:
x = a() x = a()
# use x # use x
`requiresInit` pragma can also be applyied to `distinct` types.
Given the following distinct type definitions:
.. code-block:: nim
type
DistinctObject {.requiresInit, borrow: `.`.} = distinct MyObject
DistinctString {.requiresInit.} = distinct string
The following code blocks will fail to compile:
.. code-block:: nim
var foo: DistinctFoo
foo.x = "test"
doAssert foo.x == "test"
.. code-block:: nim
var s: DistinctString
s = "test"
doAssert s == "test"
But these ones will compile successfully:
.. code-block:: nim
let foo = DistinctFoo(Foo(x: "test"))
doAssert foo.x == "test"
.. code-block:: nim
let s = "test"
doAssert s == "test"
Let statement Let statement
------------- -------------

View file

@ -106,3 +106,35 @@ type FooD = distinct int
proc `<=`(a, b: FooD): bool {.borrow.} proc `<=`(a, b: FooD): bool {.borrow.}
for f in [FooD(0): "Foo"]: echo f for f in [FooD(0): "Foo"]: echo f
block tRequiresInit:
template accept(x) =
static: doAssert compiles(x)
template reject(x) =
static: doAssert not compiles(x)
type
Foo = object
x: string
DistinctFoo {.requiresInit, borrow: `.`.} = distinct Foo
DistinctString {.requiresInit.} = distinct string
reject:
var foo: DistinctFoo
foo.x = "test"
doAssert foo.x == "test"
accept:
let foo = DistinctFoo(Foo(x: "test"))
doAssert foo.x == "test"
reject:
var s: DistinctString
s = "test"
doAssert s == "test"
accept:
let s = "test"
doAssert s == "test"