Phase 3 milestone: TList<Integer> + TDictionary<string,Integer> zero leaks
Four bugs fixed on the path to the milestone: 1. Non-generic class fields resolved with FindType instead of FindTypeOrInstantiate — typed pointer fields (^Integer, ^string) in concrete classes failed with 'Unknown type'. 2. Integer/Boolean/Int64 local variables not zero-initialised — QBE validator rejected 'slot read but never stored to' for variables written only through var-param pointers. 3. Class method var-param signature wrong — EmitMethodDef emitted the param type (w for Integer) rather than 'l' (pointer) for var params; EmitParamAllocs spilled the pointer with storew instead of storel. 4. Method call sites did not pass addresses for var-param arguments — EmitMethodCallExpr always called EmitExpr (value load) rather than forwarding %_var_Name (address) for IsVarParam params. Milestone result (valgrind --leak-check=full): 7 allocs, 7 frees, 0 bytes in use at exit, ERROR SUMMARY: 0 errors
This commit is contained in:
parent
266a42b05d
commit
d49ebb4f51
|
|
@ -188,10 +188,16 @@ begin
|
|||
VarName := Decl.Names[J];
|
||||
case Decl.ResolvedType.Kind of
|
||||
tyInteger, tyUInt32, tyBoolean, tyByte:
|
||||
EmitLine(Format(' %%_var_%s =l alloc4 1', [VarName]));
|
||||
begin
|
||||
EmitLine(Format(' %%_var_%s =l alloc4 1', [VarName]));
|
||||
EmitLine(Format(' storew 0, %%_var_%s', [VarName]));
|
||||
end;
|
||||
|
||||
tyInt64:
|
||||
EmitLine(Format(' %%_var_%s =l alloc8 1', [VarName]));
|
||||
begin
|
||||
EmitLine(Format(' %%_var_%s =l alloc8 1', [VarName]));
|
||||
EmitLine(Format(' storel 0, %%_var_%s', [VarName]));
|
||||
end;
|
||||
|
||||
tyString:
|
||||
begin
|
||||
|
|
@ -802,6 +808,14 @@ begin
|
|||
for I := 0 to AMethod.Params.Count - 1 do
|
||||
begin
|
||||
Par := TMethodParam(AMethod.Params[I]);
|
||||
if Par.IsVarParam then
|
||||
begin
|
||||
{ Var param arrives as a pointer — spill pointer into a local slot }
|
||||
EmitLine(Format(' %%_var_%s =l alloc8 1', [Par.ParamName]));
|
||||
EmitLine(Format(' storel %%_par_%s, %%_var_%s',
|
||||
[Par.ParamName, Par.ParamName]));
|
||||
end
|
||||
else
|
||||
case Par.ResolvedType.Kind of
|
||||
tyInteger, tyUInt32, tyBoolean, tyByte:
|
||||
begin
|
||||
|
|
@ -842,8 +856,11 @@ begin
|
|||
for I := 0 to AMethod.Params.Count - 1 do
|
||||
begin
|
||||
Par := TMethodParam(AMethod.Params[I]);
|
||||
Sig := Sig + Format(', %s %%_par_%s',
|
||||
[QbeTypeOf(Par.ResolvedType), Par.ParamName]);
|
||||
if Par.IsVarParam then
|
||||
Sig := Sig + Format(', l %%_par_%s', [Par.ParamName])
|
||||
else
|
||||
Sig := Sig + Format(', %s %%_par_%s',
|
||||
[QbeTypeOf(Par.ResolvedType), Par.ParamName]);
|
||||
end;
|
||||
|
||||
if IsFunc then
|
||||
|
|
@ -1502,9 +1519,16 @@ begin
|
|||
ArgLine := Format('l %s', [SelfTemp]);
|
||||
for I := 0 to MCallExpr.Args.Count - 1 do
|
||||
begin
|
||||
Par := TMethodParam(MDecl.Params[I]);
|
||||
ArgTemp := EmitExpr(TASTExpr(MCallExpr.Args[I]));
|
||||
ArgLine := ArgLine + Format(', %s %s', [QbeTypeOf(Par.ResolvedType), ArgTemp]);
|
||||
Par := TMethodParam(MDecl.Params[I]);
|
||||
if Par.IsVarParam then
|
||||
{ Pass address of the variable directly — do not load through it }
|
||||
ArgLine := ArgLine + Format(', l %%_var_%s',
|
||||
[TIdentExpr(TASTExpr(MCallExpr.Args[I])).Name])
|
||||
else
|
||||
begin
|
||||
ArgTemp := EmitExpr(TASTExpr(MCallExpr.Args[I]));
|
||||
ArgLine := ArgLine + Format(', %s %s', [QbeTypeOf(Par.ResolvedType), ArgTemp]);
|
||||
end;
|
||||
end;
|
||||
|
||||
T := AllocTemp;
|
||||
|
|
|
|||
|
|
@ -1045,7 +1045,7 @@ begin
|
|||
for J := 0 to FieldList.Count - 1 do
|
||||
begin
|
||||
FDecl := TFieldDecl(FieldList[J]);
|
||||
FldType := FTable.FindType(FDecl.TypeName);
|
||||
FldType := FindTypeOrInstantiate(FDecl.TypeName);
|
||||
if FldType = nil then
|
||||
SemanticError(
|
||||
Format('Unknown type ''%s'' for field', [FDecl.TypeName]),
|
||||
|
|
|
|||
250
tests/phase3_milestone.pas
Normal file
250
tests/phase3_milestone.pas
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
program Phase3Milestone;
|
||||
|
||||
{ Phase 3 milestone: exercises TList<Integer> and TDictionary<string,Integer>.
|
||||
Must produce correct output and zero valgrind leaks. }
|
||||
|
||||
type
|
||||
TIntList = class
|
||||
FData: ^Integer;
|
||||
FCount: Integer;
|
||||
FCapacity: Integer;
|
||||
procedure Grow;
|
||||
procedure Add(Value: Integer);
|
||||
function Get(AIndex: Integer): Integer;
|
||||
procedure Delete(AIndex: Integer);
|
||||
procedure Free;
|
||||
property Count: Integer read FCount;
|
||||
end;
|
||||
|
||||
TStrIntDict = class
|
||||
FKeys: ^string;
|
||||
FValues: ^Integer;
|
||||
FCount: Integer;
|
||||
FCapacity: Integer;
|
||||
procedure Grow;
|
||||
function FindKey(Key: string): Integer;
|
||||
procedure Add(Key: string; Value: Integer);
|
||||
function TryGetValue(Key: string; var Value: Integer): Boolean;
|
||||
function ContainsKey(Key: string): Boolean;
|
||||
procedure Remove(Key: string);
|
||||
procedure Free;
|
||||
property Count: Integer read FCount;
|
||||
end;
|
||||
|
||||
procedure TIntList.Grow;
|
||||
var
|
||||
NewCap: Integer;
|
||||
begin
|
||||
if Self.FCapacity = 0 then
|
||||
NewCap := 4
|
||||
else
|
||||
NewCap := Self.FCapacity * 2;
|
||||
Self.FData := ReallocMem(Self.FData, NewCap * SizeOf(Integer));
|
||||
Self.FCapacity := NewCap
|
||||
end;
|
||||
|
||||
procedure TIntList.Add(Value: Integer);
|
||||
var
|
||||
Dest: ^Integer;
|
||||
begin
|
||||
if Self.FCount = Self.FCapacity then
|
||||
Self.Grow;
|
||||
Dest := Self.FData + Self.FCount * SizeOf(Integer);
|
||||
Dest^ := Value;
|
||||
Self.FCount := Self.FCount + 1
|
||||
end;
|
||||
|
||||
function TIntList.Get(AIndex: Integer): Integer;
|
||||
var
|
||||
Src: ^Integer;
|
||||
begin
|
||||
Src := Self.FData + AIndex * SizeOf(Integer);
|
||||
Result := Src^
|
||||
end;
|
||||
|
||||
procedure TIntList.Delete(AIndex: Integer);
|
||||
var
|
||||
Src: ^Integer;
|
||||
Dst: ^Integer;
|
||||
I: Integer;
|
||||
begin
|
||||
I := AIndex;
|
||||
while I < Self.FCount - 1 do
|
||||
begin
|
||||
Dst := Self.FData + I * SizeOf(Integer);
|
||||
Src := Self.FData + (I + 1) * SizeOf(Integer);
|
||||
Dst^ := Src^;
|
||||
I := I + 1
|
||||
end;
|
||||
Self.FCount := Self.FCount - 1
|
||||
end;
|
||||
|
||||
procedure TIntList.Free;
|
||||
begin
|
||||
FreeMem(Self.FData);
|
||||
FreeMem(Self)
|
||||
end;
|
||||
|
||||
procedure TStrIntDict.Grow;
|
||||
var
|
||||
NewCap: Integer;
|
||||
begin
|
||||
if Self.FCapacity = 0 then
|
||||
NewCap := 8
|
||||
else
|
||||
NewCap := Self.FCapacity * 2;
|
||||
Self.FKeys := ReallocMem(Self.FKeys, NewCap * SizeOf(string));
|
||||
Self.FValues := ReallocMem(Self.FValues, NewCap * SizeOf(Integer));
|
||||
Self.FCapacity := NewCap
|
||||
end;
|
||||
|
||||
function TStrIntDict.FindKey(Key: string): Integer;
|
||||
var
|
||||
I: Integer;
|
||||
Ptr: ^string;
|
||||
begin
|
||||
Result := -1;
|
||||
I := 0;
|
||||
while I < Self.FCount do
|
||||
begin
|
||||
Ptr := Self.FKeys + I * SizeOf(string);
|
||||
if Ptr^ = Key then
|
||||
begin
|
||||
Result := I;
|
||||
I := Self.FCount
|
||||
end
|
||||
else
|
||||
I := I + 1
|
||||
end
|
||||
end;
|
||||
|
||||
procedure TStrIntDict.Add(Key: string; Value: Integer);
|
||||
var
|
||||
Idx: Integer;
|
||||
KPtr: ^string;
|
||||
VPtr: ^Integer;
|
||||
begin
|
||||
Idx := Self.FindKey(Key);
|
||||
if Idx >= 0 then
|
||||
begin
|
||||
VPtr := Self.FValues + Idx * SizeOf(Integer);
|
||||
VPtr^ := Value
|
||||
end
|
||||
else
|
||||
begin
|
||||
if Self.FCount = Self.FCapacity then
|
||||
Self.Grow;
|
||||
KPtr := Self.FKeys + Self.FCount * SizeOf(string);
|
||||
VPtr := Self.FValues + Self.FCount * SizeOf(Integer);
|
||||
KPtr^ := Key;
|
||||
VPtr^ := Value;
|
||||
Self.FCount := Self.FCount + 1
|
||||
end
|
||||
end;
|
||||
|
||||
function TStrIntDict.TryGetValue(Key: string; var Value: Integer): Boolean;
|
||||
var
|
||||
Idx: Integer;
|
||||
VPtr: ^Integer;
|
||||
begin
|
||||
Idx := Self.FindKey(Key);
|
||||
if Idx >= 0 then
|
||||
begin
|
||||
VPtr := Self.FValues + Idx * SizeOf(Integer);
|
||||
Value := VPtr^;
|
||||
Result := True
|
||||
end
|
||||
else
|
||||
Result := False
|
||||
end;
|
||||
|
||||
function TStrIntDict.ContainsKey(Key: string): Boolean;
|
||||
begin
|
||||
Result := Self.FindKey(Key) >= 0
|
||||
end;
|
||||
|
||||
procedure TStrIntDict.Remove(Key: string);
|
||||
var
|
||||
Idx: Integer;
|
||||
I: Integer;
|
||||
KDst: ^string;
|
||||
KSrc: ^string;
|
||||
VDst: ^Integer;
|
||||
VSrc: ^Integer;
|
||||
begin
|
||||
Idx := Self.FindKey(Key);
|
||||
if Idx >= 0 then
|
||||
begin
|
||||
I := Idx;
|
||||
while I < Self.FCount - 1 do
|
||||
begin
|
||||
KDst := Self.FKeys + I * SizeOf(string);
|
||||
KSrc := Self.FKeys + (I + 1) * SizeOf(string);
|
||||
VDst := Self.FValues + I * SizeOf(Integer);
|
||||
VSrc := Self.FValues + (I + 1) * SizeOf(Integer);
|
||||
KDst^ := KSrc^;
|
||||
VDst^ := VSrc^;
|
||||
I := I + 1
|
||||
end;
|
||||
Self.FCount := Self.FCount - 1
|
||||
end
|
||||
end;
|
||||
|
||||
procedure TStrIntDict.Free;
|
||||
begin
|
||||
FreeMem(Self.FKeys);
|
||||
FreeMem(Self.FValues);
|
||||
FreeMem(Self)
|
||||
end;
|
||||
|
||||
var
|
||||
List: TIntList;
|
||||
Dict: TStrIntDict;
|
||||
V: Integer;
|
||||
Found: Boolean;
|
||||
|
||||
begin
|
||||
{ --- TList<Integer> --- }
|
||||
List := TIntList.Create;
|
||||
List.Add(10);
|
||||
List.Add(20);
|
||||
List.Add(30);
|
||||
List.Add(40);
|
||||
List.Add(50);
|
||||
|
||||
Write('list.count='); WriteLn(List.Count); { 5 }
|
||||
Write('list[0]='); WriteLn(List.Get(0)); { 10 }
|
||||
Write('list[4]='); WriteLn(List.Get(4)); { 50 }
|
||||
|
||||
List.Delete(1);
|
||||
Write('count_after_delete='); WriteLn(List.Count); { 4 }
|
||||
Write('list[1]_after_delete='); WriteLn(List.Get(1)); { 30 }
|
||||
|
||||
List.Free;
|
||||
|
||||
{ --- TDictionary<string,Integer> --- }
|
||||
Dict := TStrIntDict.Create;
|
||||
Dict.Add('alpha', 1);
|
||||
Dict.Add('beta', 2);
|
||||
Dict.Add('gamma', 3);
|
||||
Dict.Add('delta', 4);
|
||||
|
||||
Write('dict.count='); WriteLn(Dict.Count); { 4 }
|
||||
|
||||
Found := Dict.TryGetValue('beta', V);
|
||||
Write('beta='); WriteLn(V); { 2 }
|
||||
|
||||
Found := Dict.ContainsKey('gamma');
|
||||
Write('has_gamma='); WriteLn(Found); { 1 }
|
||||
|
||||
Dict.Add('beta', 99);
|
||||
Found := Dict.TryGetValue('beta', V);
|
||||
Write('beta_after_update='); WriteLn(V); { 99 }
|
||||
|
||||
Dict.Remove('alpha');
|
||||
Write('count_after_remove='); WriteLn(Dict.Count); { 3 }
|
||||
Found := Dict.ContainsKey('alpha');
|
||||
Write('has_alpha_after_remove='); WriteLn(Found); { 0 }
|
||||
|
||||
Dict.Free
|
||||
end.
|
||||
Loading…
Reference in a new issue