zevv/with

Introducing with for Object members (property) cannot be assigned to when used with with

Opened this issue · 5 comments

Before

proc vmAdd(self: Computer) =
  self.s.processor.memory3 = self.s.processor.memory1 + self.s.processor.memory2
  self.s.processor.programCounter = self.s.processor.programCounter + 4

after

proc vmAdd(self: Computer) =
  with self.s.processor:
    memory3 = memory1 + memory2
    programCounter = programCounter + 4

In the after code the s of the self in the with statement is underlined issue reported is template/generic instantiation of with from here

memory3 and ProgramCounter are underlined with
'memory3' cannot be assigned to
'programCounter' cannot be assigned to

Please note that I in in the very early days of learning nim so its entirely possible I've made mistakes. The Advent of code problem to which the above code belongs compiles and gives the correct answers.

related code where with seems to work fine

type
  OutputResponse* = enum
    HaltOnOutput = 1,
    ContinueOnOutput = 2,

type
  Properties = ref object
    actionOnOutput: OutputResponse
    runHasCompleted: bool
    input: seq[int64]
    output: seq[int64]

type
  State = ref object
    newOutputReady: bool
    processor: IntComputerProgramCounterFrame

type
  Computer* = ref object
    p: Properties
    s: State


proc initProperties(): Properties =
  result = new Properties
  with result:
    actionOnOutput = OutputResponse.HaltOnOutput
    runHasCompleted = false
    input = @[]
    output = @[]

proc initState(): State =
  result = new State
  with result:
    newOutputReady = false
    processor = newIntComputerProgramCounterFrame()

proc initComputer*(): Computer =
  result = new Computer
  with result:
    p = initProperties()
    s = initState()
zevv commented

It's hard to deduce what your exact problem is from the snippets above because it does not include all type definitions, but if I complete it as below, everything works as expected:

import with

type

  Processor = ref object
    programCounter: int
    memory1, memory2, memory3: int

  Thing = ref object
    processor: Processor

  Computer = ref object
    s: Thing


proc vmAdd(self: Computer) = 
  with self.s.processor:                                                        
    memory3 = memory1 + memory2                                        
    programCounter = programCounter + 4
                             
var c = Computer(s: Thing(processor: Processor()))
                                                                                
c.s.processor.memory1 = 1                                              
c.s.processor.memory2 = 1
                                                  
vmAdd(c)                
                               
echo c.repr           

Could it be that you are passing objects to vmAdd, instead of ref objects? In this case you should define your proc to take a var as argument like so:

proc vmAdd(self: var Computer)
   ... 

I'd be glad to help you out with more details, but it would help if you could provide me with a full example with which I can fully reproduce your issue.

Please find attached the complete definitions of IntComputer and IntCOmputerProgramCounterFrame and the day2 code and input. I very much appreciate your taking the time to look at this issue.
IntComputer.zip

zevv commented

Ok, I admit with is at fault here, as it does not properly understand your intentions here. It is a nice corner case here, so I'll try to see if I can get this to work.

Below is a standalone snippet that shows what is happening: the val=() method is not properly handled by with as it is not detected by the macro which generates the hidden access templates.

If I can't fix this I'm sure @PMunch can :)

Thanks for the report!

import with

type        
  Thing = ref object
    hiddenVal: int
    
  Box = ref object 
    thing: Thing
    
proc val(t: Thing): int =
  t.hiddenVal
  
proc `val=`(t: Thing, v:int) =
  t.hiddenVal = v 
  
let box = Box(thing: Thing())

box.thing.val = 3
echo box.thing.val

with box:
  thing.val = 4
  echo thing.val
  
with box.thing:
  val = 4
  echo val       # <---- BOOM

Hmm, this is tricky because there is no easy way that I know of to get procedures that take a given type.. I might be wrong, but this could potentially be super hard to fix.

zevv commented

That was my thought as well, Nim lacks the introspection to find out what procs or templates "belong" to a given type.