Node immutability improved

This commit is contained in:
Michael Schimmel
2025-11-05 18:13:43 +01:00
parent 0915d6d90d
commit 1f0eef7698
11 changed files with 409 additions and 308 deletions
+33 -43
View File
@@ -111,7 +111,7 @@ begin
// Get the type from the descriptor (which was populated by Binder/RTL)
symbol := FCurrentDescriptor.FindSymbol(Node.Name);
adr := Node.AsIdentifier.Address;
adr := Node.Address;
// Create a new node, copying the address and assigning the inferred type
Result := TAst.Identifier(Node.Name, adr, symbol.StaticType);
@@ -135,9 +135,7 @@ function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationN
var
initType: IStaticType;
newInitializer, newIdent: IAstNode;
boundIdent: IIdentifierNode;
adr: TResolvedAddress;
N: TVariableDeclarationNode;
begin
// 1. Visit Initializer first (if it exists)
if Assigned(Node.Initializer) then
@@ -151,23 +149,24 @@ begin
else
initType := TTypes.Unknown; // (def fib)
// 3. Get the address from the bound identifier (SAFE CAST)
boundIdent := Node.Identifier;
adr := boundIdent.AsIdentifier.Address;
// 3. Get the address from the bound identifier
adr := Node.Identifier.Address;
// 4. Update the type in the scope descriptor (which was set to Unknown by the binder).
if initType.Kind <> stUnknown then
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
// 5. Create the new (typed) identifier node
newIdent := TAst.Identifier(boundIdent.Name, adr, initType);
newIdent := TAst.Identifier(Node.Identifier.Name, adr, initType);
// 6. Create the new VariableDeclaration node
Result := TVariableDeclarationNode.Create(newIdent.AsIdentifier, newInitializer, initType);
// 7. Copy runtime flags (IsBoxed)
N := (Result as TVariableDeclarationNode);
N.IsBoxed := (Node as TVariableDeclarationNode).IsBoxed;
// 6. Create the new VariableDeclaration node using the factory
Result :=
TAst.VarDecl(
newIdent.AsIdentifier,
newInitializer,
initType,
Node.IsBoxed // 7. Copy runtime flags (IsBoxed) via interface
);
end;
function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
@@ -206,7 +205,6 @@ end;
function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
boundNode: TLambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
bodyType, methodType: IStaticType;
@@ -214,22 +212,20 @@ var
i: Integer;
savedDescriptor: IScopeDescriptor;
begin
boundNode := (Node as TLambdaExpressionNode);
// 1. Enter the lambda's scope (which Binder already created)
savedDescriptor := FCurrentDescriptor;
FCurrentDescriptor := boundNode.ScopeDescriptor;
FCurrentDescriptor := Node.ScopeDescriptor;
try
// 2. Visit parameters (they are already bound, just need typing)
SetLength(newParams, Length(boundNode.Parameters));
SetLength(paramTypes, Length(boundNode.Parameters));
SetLength(newParams, Length(Node.Parameters));
SetLength(paramTypes, Length(Node.Parameters));
for i := 0 to High(boundNode.Parameters) do
for i := 0 to High(Node.Parameters) do
begin
// Parameters are leaves, but we must *replace* them with typed versions
// (even if they are just TTypes.Unknown for now, for type inference placeholders)
var paramIdent := boundNode.Parameters[i];
var paramAdr := paramIdent.AsIdentifier.Address;
var paramIdent := Node.Parameters[i];
var paramAdr := paramIdent.Address;
var newParam := TAst.Identifier(paramIdent.Name, paramAdr);
newParams[i] := newParam;
@@ -237,7 +233,7 @@ begin
end;
// 3. Visit the body to infer its return type
newBody := Accept(boundNode.Body);
newBody := Accept(Node.Body);
bodyType := newBody.AsTypedNode.StaticType;
// 4. Create the final method type
@@ -251,14 +247,8 @@ begin
FCurrentDescriptor := savedDescriptor;
end;
// 7. Create the new (typed) lambda node
Result := TLambdaExpressionNode.Create(newParams, newBody, methodType);
// 8. Copy runtime properties
var newLambda := (Result as TLambdaExpressionNode);
newLambda.ScopeDescriptor := boundNode.ScopeDescriptor;
newLambda.Upvalues := boundNode.Upvalues;
newLambda.HasNestedLambdas := boundNode.HasNestedLambdas;
// 7. Create the new (typed) lambda node using the factory
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, methodType);
end;
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
@@ -300,11 +290,14 @@ begin
else if calleeType.Kind <> TStaticTypeKind.stUnknown then
raise ETypeException.CreateFmt('Cannot invoke type %s as a function.', [calleeType.ToString]);
// 4. Create the new (typed) call node
Result := TFunctionCallNode.Create(newCallee, newArgs, retType);
// 5. Copy runtime properties
(Result as TFunctionCallNode).IsTailCall := (Node as TFunctionCallNode).IsTailCall;
// 4. Create the new (typed) call node using the factory
Result :=
TAst.FunctionCall(
newCallee,
newArgs,
retType,
Node.IsTailCall // 5. Copy runtime properties
);
end;
function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
@@ -508,17 +501,14 @@ var
valType: IStaticType;
scalarKind: TScalar.TKind;
allScalar: Boolean;
oldNode: TGenericRecordLiteralNode; // Parser creates this type
newFields: TArray<TRecordFieldLiteral>;
begin
oldNode := (Node as TGenericRecordLiteralNode);
// 1. Visit all child nodes first to infer their types
SetLength(newFields, Length(oldNode.Fields));
for i := 0 to High(oldNode.Fields) do
SetLength(newFields, Length(Node.Fields));
for i := 0 to High(Node.Fields) do
begin
newFields[i].Key := Accept(oldNode.Fields[i].Key).AsKeyword;
newFields[i].Value := Accept(oldNode.Fields[i].Value);
newFields[i].Key := Accept(Node.Fields[i].Key).AsKeyword;
newFields[i].Value := Accept(Node.Fields[i].Value);
end;
SetLength(scalarDefFields, Length(newFields));