diff --git a/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas b/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas index b515bbc..91200a0 100644 --- a/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas +++ b/compiler/src/main/pascal/blaise.codegen.native.x86_64.pas @@ -12701,12 +12701,24 @@ begin Self.Emit(#9'pushq %rax'); Self.EmitExprToEax(SSA.IndexExpr); { index } Self.Emit(#9'pushq %rax'); - if Self.IsLocal(SSA.ArrayName) then - Self.Emit(Format(#9'movq %s, %%rdi', [Self.VarOperand(SSA.ArrayName)])) + if SSA.IsImplicitSelf then + begin + { Self.Field[I] := V — load Self, reach the field slot, deref to the + field's class object (the setter receiver). } + Self.Emit(Format(#9'movq %s, %%rdi', [Self.VarOperand('Self')])); + if SSA.ImplicitFieldInfo.Offset > 0 then + Self.Emit(Format(#9'addq $%d, %%rdi', [SSA.ImplicitFieldInfo.Offset])); + Self.Emit(#9'movq (%rdi), %rdi'); + end else - Self.Emit(Format(#9'movq %s(%%rip), %%rdi', [SSA.ArrayName])); - if SSA.IsVarParam then - Self.Emit(#9'movq (%rdi), %rdi'); { var-param: slot -> instance } + begin + if Self.IsLocal(SSA.ArrayName) then + Self.Emit(Format(#9'movq %s, %%rdi', [Self.VarOperand(SSA.ArrayName)])) + else + Self.Emit(Format(#9'movq %s(%%rip), %%rdi', [SSA.ArrayName])); + if SSA.IsVarParam then + Self.Emit(#9'movq (%rdi), %rdi'); { var-param: slot -> instance } + end; Self.Emit(#9'popq %rsi'); { index } Self.Emit(#9'popq %rdx'); { value } Self.EmitPropAccessorCallNative(SSA.PropOwnerType, diff --git a/compiler/src/main/pascal/blaise.codegen.qbe.pas b/compiler/src/main/pascal/blaise.codegen.qbe.pas index 6d0d5ad..1e294c2 100644 --- a/compiler/src/main/pascal/blaise.codegen.qbe.pas +++ b/compiler/src/main/pascal/blaise.codegen.qbe.pas @@ -14703,14 +14703,34 @@ begin if AStmt.PropWriteInfo <> nil then begin DefProp := TPropertyInfo(AStmt.PropWriteInfo); - RecvTemp := AllocTemp(); - EmitLine(Format(' %s =l loadl %s', - [RecvTemp, VarRef(AStmt.ArrayName, AStmt.IsGlobal)])); - if AStmt.IsVarParam then + if AStmt.IsImplicitSelf then begin + { Self.Field[I] := V — load Self, reach the field slot, deref to the + field's class object (the setter receiver). } + RecvTemp := AllocTemp(); + EmitLine(Format(' %s =l loadl %%_var_Self', [RecvTemp])); + if AStmt.ImplicitFieldInfo.Offset > 0 then + begin + ElemPtr := AllocTemp(); + EmitLine(Format(' %s =l add %s, %d', + [ElemPtr, RecvTemp, AStmt.ImplicitFieldInfo.Offset])); + RecvTemp := ElemPtr; + end; ElemPtr := AllocTemp(); EmitLine(Format(' %s =l loadl %s', [ElemPtr, RecvTemp])); RecvTemp := ElemPtr; + end + else + begin + RecvTemp := AllocTemp(); + EmitLine(Format(' %s =l loadl %s', + [RecvTemp, VarRef(AStmt.ArrayName, AStmt.IsGlobal)])); + if AStmt.IsVarParam then + begin + ElemPtr := AllocTemp(); + EmitLine(Format(' %s =l loadl %s', [ElemPtr, RecvTemp])); + RecvTemp := ElemPtr; + end; end; IdxW := EmitExpr(AStmt.IndexExpr); IdxQType := QbeTypeOf(DefProp.IndexTypeDesc); diff --git a/compiler/src/main/pascal/uSemantic.pas b/compiler/src/main/pascal/uSemantic.pas index a2b4958..00175c7 100644 --- a/compiler/src/main/pascal/uSemantic.pas +++ b/compiler/src/main/pascal/uSemantic.pas @@ -12115,6 +12115,31 @@ begin Format('''%s'' element', [AStmt.ArrayName]), AStmt.Line, AStmt.Col); Exit; end; + { Implicit Self.Field[I] := V where Field is a class with a writable + default array property — lower to its setter on the field's object. } + if (BaseInfo <> nil) and (BaseInfo.TypeDesc <> nil) and + (BaseInfo.TypeDesc.Kind = tyClass) then + begin + DefProp := TRecordTypeDesc(BaseInfo.TypeDesc).FindDefaultProperty(); + if (DefProp <> nil) and (DefProp.WriteMethod <> '') then + begin + AStmt.IsImplicitSelf := True; + AStmt.ImplicitFieldInfo := BaseInfo; + AStmt.PropWriteInfo := DefProp; + AStmt.PropOwnerType := PropAccessorOwner( + TRecordTypeDesc(BaseInfo.TypeDesc).Name, DefProp.WriteMethod); + AStmt.PropAccessorVSlot := PropAccessorVSlot( + TRecordTypeDesc(BaseInfo.TypeDesc).Name, DefProp.WriteMethod); + IdxType := AnalyseExpr(AStmt.IndexExpr); + if DefProp.IndexTypeDesc <> nil then + CheckTypesMatch(DefProp.IndexTypeDesc, IdxType, 'default property index', + AStmt.Line, AStmt.Col); + ValType := AnalyseExpr(AStmt.ValueExpr); + CheckTypesMatch(DefProp.TypeDesc, ValType, 'default property assignment', + AStmt.Line, AStmt.Col); + Exit; + end; + end; end; SemanticError( Format('Undeclared variable ''%s''', [AStmt.ArrayName]), diff --git a/compiler/src/test/pascal/cp.test.e2e.properties.pas b/compiler/src/test/pascal/cp.test.e2e.properties.pas index 2927cda..4d1d0c4 100644 --- a/compiler/src/test/pascal/cp.test.e2e.properties.pas +++ b/compiler/src/test/pascal/cp.test.e2e.properties.pas @@ -43,6 +43,9 @@ type Obj.Field[I] := V and Obj.Prop[I] := V. } procedure TestRun_DefaultProperty_WriteThroughField; procedure TestRun_DefaultProperty_WriteThroughProperty; + { Implicit-Self write/read: Field[I] := V / Field[I] inside a method, where + Field is a class with a default array property. } + procedure TestRun_DefaultProperty_WriteThroughImplicitSelf; { Static-array field accessed from inside a method (implicit Self). } procedure TestRun_StaticArrayField_ReadInMethod; procedure TestRun_StaticArrayField_WriteInMethod; @@ -439,6 +442,41 @@ begin AssertRunsOnAll(SrcDefaultWriteProp, '9' + LE, 0); end; +const + { Implicit Self.Field[I] := V and Field[I] read, inside methods. } + SrcDefaultWriteImplicitSelf = ''' + program P; + type + TVec = class + FData: array[0..4] of Integer; + function Get(i: Integer): Integer; begin Result := FData[i] end; + procedure Put(i: Integer; v: Integer); begin FData[i] := v end; + property Items[i: Integer]: Integer read Get write Put; default; + end; + TOwner = class + FVec: TVec; + constructor Create; + procedure Fill; + function Sum: Integer; + end; + constructor TOwner.Create; begin inherited Create; FVec := TVec.Create end; + procedure TOwner.Fill; begin FVec[0] := 3; FVec[1] := 4; end; + function TOwner.Sum: Integer; begin Result := FVec[0] + FVec[1]; end; + var o: TOwner; + begin + o := TOwner.Create; + o.Fill(); + WriteLn(o.Sum()); + o := nil + end. + '''; + +procedure TE2EPropertyTests.TestRun_DefaultProperty_WriteThroughImplicitSelf; +begin + if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end; + AssertRunsOnAll(SrcDefaultWriteImplicitSelf, '7' + LE, 0); +end; + initialization RegisterTest(TE2EPropertyTests);