mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 02:43:41 +00:00
Compare commits
14 Commits
797b05eda6
...
pr_object
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1204f4a38e | ||
|
|
a891b0d948 | ||
|
|
a8c9e9c4e9 | ||
|
|
d997bd4cb5 | ||
|
|
e51f27c806 | ||
|
|
c1c53bebf1 | ||
|
|
bde9abb2f7 | ||
|
|
3c36fc17dc | ||
|
|
6f8b9b7cc4 | ||
|
|
a56697b513 | ||
|
|
24857a0162 | ||
|
|
91dee1bcff | ||
|
|
251fec94ce | ||
|
|
0175be50a9 |
@@ -3078,7 +3078,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
result = semConv(c, n, expectedType)
|
||||
elif ambig and n.len == 1:
|
||||
errorUseQualifier(c, n.info, s)
|
||||
elif n.len == 1:
|
||||
elif n.len == 1 or (n.kind == nkCall and useObjConstr(c, n, flags, expectedType)):
|
||||
result = semObjConstr(c, n, flags, expectedType)
|
||||
elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
|
||||
else: result = semMagic(c, n, s, flags, expectedType)
|
||||
|
||||
@@ -506,12 +506,12 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
result[i] = semGenericStmt(c, n[i], flags, ctx)
|
||||
if result[0].kind == nkSym:
|
||||
let fmoduleId = getModule(result[0].sym).id
|
||||
var isVisable = false
|
||||
var isVisible = false
|
||||
for module in c.friendModules:
|
||||
if module.id == fmoduleId:
|
||||
isVisable = true
|
||||
isVisible = true
|
||||
break
|
||||
if isVisable:
|
||||
if isVisible:
|
||||
for i in 1..<result.len:
|
||||
if result[i].kind == nkExprColonExpr:
|
||||
result[i][1].flags.incl nfSkipFieldChecking
|
||||
|
||||
@@ -412,15 +412,113 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
|
||||
else:
|
||||
assert false, "Must not enter here."
|
||||
|
||||
type
|
||||
ObjConstrError = enum
|
||||
none
|
||||
discriminatorError = "The discriminator can only be initialized with unnamed fields known at the compile time"
|
||||
mixingError = "When mixing named fields and unnamed fields, every field needs to be initialized in order"
|
||||
lackingError = "The object construction is given more fields than required"
|
||||
|
||||
proc filterObjConstr(c: PContext; field: PNode, n: PNode, iterField: var int, flags: TExprFlags, write: bool): ObjConstrError =
|
||||
result = none
|
||||
if iterField >= n.len:
|
||||
return mixingError
|
||||
case field.kind
|
||||
of nkRecCase:
|
||||
# handle defaults if the ast of the field is known
|
||||
var discriminatorVal =
|
||||
case n[iterField].kind
|
||||
of nkExprColonExpr:
|
||||
semExprFlagDispatched(c, n[iterField][1], flags + {efPreferStatic})
|
||||
else:
|
||||
semExprFlagDispatched(c, n[iterField], flags + {efPreferStatic})
|
||||
|
||||
let ret = filterObjConstr(c, field[0], n, iterField, flags, write)
|
||||
if ret != none:
|
||||
return ret
|
||||
|
||||
if discriminatorVal == nil or discriminatorVal.kind != nkIntLit:
|
||||
return discriminatorError
|
||||
|
||||
let matchedBranch = field.pickCaseBranch discriminatorVal
|
||||
if matchedBranch != nil:
|
||||
result = filterObjConstr(c, matchedBranch.lastSon, n, iterField, flags, write)
|
||||
else:
|
||||
result = none
|
||||
|
||||
of nkSym:
|
||||
if n[iterField].kind == nkExprColonExpr and field.sym.name.id == considerQuotedIdent(c, n[iterField][0]).id:
|
||||
inc iterField
|
||||
elif not fieldVisible(c, field.sym):
|
||||
discard
|
||||
elif n[iterField].kind != nkExprColonExpr:
|
||||
if write:
|
||||
n[iterField] = newTree(nkExprColonExpr, field, n[iterField])
|
||||
inc iterField
|
||||
else:
|
||||
result = mixingError
|
||||
of nkRecList:
|
||||
for f in field:
|
||||
let ret = filterObjConstr(c, f, n, iterField, flags, write)
|
||||
if ret != none:
|
||||
result = ret
|
||||
break
|
||||
else:
|
||||
assert false
|
||||
|
||||
proc expandObjConstr(c: PContext, n: PNode, t: PType, flags: TExprFlags): PNode =
|
||||
result = n
|
||||
var hasValue = false
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind != nkExprColonExpr:
|
||||
hasValue = true
|
||||
break
|
||||
if hasValue:
|
||||
var iterField = 1
|
||||
let ret = filterObjConstr(c, t.n, result, iterField, flags, write = true)
|
||||
if ret != none:
|
||||
localError(c.config, result.info, $ret)
|
||||
else:
|
||||
if iterField > result.len:
|
||||
localError(c.config, result.info, $mixingError)
|
||||
elif iterField < result.len:
|
||||
localError(c.config, result.info, $lackingError)
|
||||
|
||||
proc useObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): bool =
|
||||
var n = copyTree(n)
|
||||
var t = semTypeNode(c, n[0], nil)
|
||||
if t == nil:
|
||||
return false
|
||||
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
|
||||
t = expectedType
|
||||
|
||||
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if t.kind == tyRef:
|
||||
t = skipTypes(t[0], {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
|
||||
if t.kind != tyObject:
|
||||
return false
|
||||
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind == nkExprColonExpr:
|
||||
return true
|
||||
|
||||
var iterField = 1
|
||||
result = filterObjConstr(c, t.n, n, iterField, flags, write = false) == none
|
||||
if iterField != n.len:
|
||||
return false
|
||||
|
||||
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
var t = semTypeNode(c, n[0], nil)
|
||||
result = newNodeIT(nkObjConstr, n.info, t)
|
||||
for i in 0..<n.len:
|
||||
result.add n[i]
|
||||
|
||||
if t == nil:
|
||||
return localErrorNode(c, result, "object constructor needs an object type")
|
||||
|
||||
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
@@ -443,6 +541,10 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
"'; the object's generic parameters cannot be inferred and must be explicitly given"
|
||||
)
|
||||
|
||||
let expanded = expandObjConstr(c, n, t, flags)
|
||||
for i in 0..<expanded.len:
|
||||
result.add expanded[i]
|
||||
|
||||
# Check if the object is fully initialized by recursively testing each
|
||||
# field (if this is a case object, initialized fields in two different
|
||||
# branches will be reported as an error):
|
||||
|
||||
@@ -1828,6 +1828,23 @@ an `object` type or a `ref object` type:
|
||||
Note that, unlike tuples, objects require the field names along with their values.
|
||||
For a `ref object` type `system.new` is invoked implicitly.
|
||||
|
||||
The field names can be omitted if all the values are given in order. It can be mixed with field names along with values.
|
||||
|
||||
```nim
|
||||
var a1 = Student("Anton", 5)
|
||||
var a2 = PStudent("Anton", age: 5)
|
||||
```
|
||||
|
||||
Note that, objects with only one field must use field names along with values. Otherwise, they will be recognized as type conversions.
|
||||
|
||||
```nim
|
||||
type
|
||||
Teacher = object
|
||||
name: string
|
||||
# var t = Teacher("lisa") # Error: type mismatch: got 'string' for '"lisa"'
|
||||
# but expected 'Teacher = object'
|
||||
var t = Teacher(name: "lisa")
|
||||
```
|
||||
|
||||
Object variants
|
||||
---------------
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
errormsg: "incorrect object construction syntax"
|
||||
errormsg: "When mixing named fields and unnamed fields, every field needs to be initialized in order"
|
||||
file: "t5965_1.nim"
|
||||
line: 10
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
errormsg: "incorrect object construction syntax"
|
||||
errormsg: "The object construction is given more fields than required"
|
||||
file: "t5965_2.nim"
|
||||
line: 10
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
type
|
||||
Noice* = object
|
||||
hidden: int
|
||||
|
||||
template jjj*: Noice =
|
||||
Noice(hidden: 15)
|
||||
|
||||
Ciao* = object
|
||||
hidden1: int
|
||||
hidden2: int
|
||||
|
||||
Gull* = ref object
|
||||
hidden1: int
|
||||
field*: int
|
||||
hidden2: int
|
||||
field2*: int
|
||||
|
||||
|
||||
template jjj*(): Noice =
|
||||
var x = 7
|
||||
Noice(hidden: 15)
|
||||
|
||||
template said*(): Ciao =
|
||||
var x = 7
|
||||
Ciao(hidden1: 15 + x, 1)
|
||||
|
||||
proc foo*: Gull =
|
||||
result = Gull(1, 2, 3, 4)
|
||||
|
||||
@@ -3,7 +3,19 @@ import m3770
|
||||
|
||||
doAssert $jjj() == "(hidden: 15)" # works
|
||||
|
||||
doAssert $said() == "(hidden1: 22, hidden2: 1)"
|
||||
|
||||
proc someGeneric(_: type) =
|
||||
doAssert $jjj() == "(hidden: 15)" # fails: "Error: the field 'hidden' is not accessible."
|
||||
when false: # todo somehow make it work?
|
||||
doAssert $said() == "(hidden1: 22, hidden2: 1)"
|
||||
|
||||
someGeneric(int)
|
||||
|
||||
doAssert $(foo()[]) == "(hidden1: 1, field: 2, hidden2: 3, field2: 4)"
|
||||
|
||||
proc bar() =
|
||||
var s = Gull(13, 14)
|
||||
doAssert $(s[]) == "(hidden1: 0, field: 13, hidden2: 0, field2: 14)"
|
||||
|
||||
bar()
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
errormsg: ""
|
||||
nimout: '''
|
||||
t17437.nim(20, 16) Error: undeclared identifier: 'x'
|
||||
t17437.nim(20, 16) Error: expression 'x' has no type (or is ambiguous)
|
||||
t17437.nim(20, 19) Error: incorrect object construction syntax
|
||||
t17437.nim(20, 19) Error: incorrect object construction syntax
|
||||
t17437.nim(20, 12) Error: expression '' has no type (or is ambiguous)
|
||||
'''
|
||||
"""
|
||||
|
||||
# bug #17437 invalid object construction should result in error
|
||||
|
||||
type
|
||||
@@ -17,6 +5,8 @@ type
|
||||
x, y: int
|
||||
|
||||
proc m =
|
||||
var x = 12
|
||||
var y = 1
|
||||
var v = V(x: x, y)
|
||||
|
||||
m()
|
||||
|
||||
39
tests/spec/mobjectconstr_unnamed.nim
Normal file
39
tests/spec/mobjectconstr_unnamed.nim
Normal file
@@ -0,0 +1,39 @@
|
||||
type
|
||||
Standard* = object
|
||||
name: string
|
||||
id*: int
|
||||
owner*: string
|
||||
|
||||
Color1* = enum
|
||||
Red, Blue, Green
|
||||
|
||||
Case1* = object
|
||||
name*: string
|
||||
id*: int
|
||||
color*: Color1
|
||||
owner: string
|
||||
|
||||
|
||||
## inplace object construction works
|
||||
doAssert Standard("Tree", 1, "sky") == Standard(name: "Tree", id: 1, owner: "sky")
|
||||
|
||||
proc initStandard*(name: string, id: int, owner: string): Standard =
|
||||
Standard(name, id, owner)
|
||||
|
||||
## It works in the procs
|
||||
doAssert initStandard("Tree", 1, "sky") == Standard(name: "Tree", id: 1, owner: "sky")
|
||||
static: doAssert initStandard("Tree", 1, "sky") == Standard(name: "Tree", id: 1, owner: "sky")
|
||||
|
||||
template toStandard*(name: string, id: int, owner: string): Standard =
|
||||
Standard(name, id, owner)
|
||||
|
||||
## It works in the procs
|
||||
doAssert toStandard("Tree", 1, "sky") == Standard(name: "Tree", id: 1, owner: "sky")
|
||||
static: doAssert toStandard("Tree", 1, "sky") == Standard(name: "Tree", id: 1, owner: "sky")
|
||||
|
||||
proc initColorRed*(name: string = "red", id: int = 1314, owner: string): Case1 =
|
||||
result = Case1(name, id, Red, owner)
|
||||
|
||||
doAssert Case1("red", 1314, color: Red, owner: "unknown") == Case1("red", 1314, color: Red, "unknown")
|
||||
doAssert Case1("red", 1314, Red, owner: "unknown") == Case1("red", 1314, Red, "unknown")
|
||||
doAssert initColorRed(owner = "unknown") == Case1("red", id: 1314, Red, "unknown")
|
||||
90
tests/spec/tobjectconstr_unnamed.nim
Normal file
90
tests/spec/tobjectconstr_unnamed.nim
Normal file
@@ -0,0 +1,90 @@
|
||||
import mobjectconstr_unnamed
|
||||
|
||||
type
|
||||
Vector = object
|
||||
a: int = 999
|
||||
b, c: int
|
||||
|
||||
block: # positional construction
|
||||
## It specifies all the unnamed fields
|
||||
var x = Vector(1, 2, 3)
|
||||
doAssert x.b == 2
|
||||
|
||||
block:
|
||||
## unnamed fields can be mixed with named fields
|
||||
block:
|
||||
var x = Vector(a: 1, 2, 3)
|
||||
doAssert x.c == 3
|
||||
|
||||
block:
|
||||
var x = Vector(1, b: 2, 3)
|
||||
doAssert x.c == 3
|
||||
|
||||
block:
|
||||
var x = Vector(1, 2, c: 3)
|
||||
doAssert x.c == 3
|
||||
|
||||
block:
|
||||
## Object variants support unnamed fields for tags, which should be known at the compile time.
|
||||
type
|
||||
Color = enum
|
||||
Red, Blue, Yellow
|
||||
Factor = object
|
||||
id: int
|
||||
case flag: Color
|
||||
of Red:
|
||||
num: int
|
||||
of Blue, Yellow:
|
||||
done: bool
|
||||
name: string
|
||||
|
||||
block:
|
||||
var x = Factor(1, Red, 2, "1314")
|
||||
doAssert x.num == 2
|
||||
|
||||
block:
|
||||
var x = Factor(1, Blue, true, "1314")
|
||||
doAssert x.done == true
|
||||
|
||||
block:
|
||||
var x = Factor(1, Yellow, false, "1314")
|
||||
doAssert x.done == false
|
||||
|
||||
|
||||
type
|
||||
Ciao = object
|
||||
id: int
|
||||
case flag: bool = false
|
||||
of true:
|
||||
num: int
|
||||
of false:
|
||||
done: bool
|
||||
name: string
|
||||
|
||||
block:
|
||||
var x = Ciao(12, false, false, "123")
|
||||
doAssert x.done == false
|
||||
|
||||
block:
|
||||
var x = Ciao(12, flag: true, 1, "123")
|
||||
doAssert x.num == 1
|
||||
|
||||
## It works in the third module
|
||||
block:
|
||||
doAssert initStandard("", 1, "sky") == Standard(id: 1, owner: "sky")
|
||||
doAssert initStandard("", 1, "sky") == Standard(1, "sky")
|
||||
doAssert toStandard("", 1, "sky") == Standard(1, "sky")
|
||||
|
||||
proc foo() =
|
||||
doAssert initStandard("", 1, "sky") == Standard(id: 1, owner: "sky")
|
||||
doAssert initStandard("", 1, "sky") == Standard(1, "sky")
|
||||
doAssert toStandard("", 1, "sky") == Standard(1, "sky")
|
||||
|
||||
foo()
|
||||
|
||||
template bar() =
|
||||
doAssert initStandard("", 1, "sky") == Standard(id: 1, owner: "sky")
|
||||
doAssert initStandard("", 1, "sky") == Standard(1, "sky")
|
||||
doAssert toStandard("", 1, "sky") == Standard(1, "sky")
|
||||
|
||||
bar()
|
||||
Reference in New Issue
Block a user