AST Identities

This commit is contained in:
Michael Schimmel
2025-11-25 19:41:26 +01:00
parent 0b7a60e338
commit aff4cec7d5
9 changed files with 2334 additions and 2150 deletions
+87 -48
View File
@@ -12,6 +12,7 @@ uses
Myc.Ast.Visitor,
Myc.Ast.Scope,
Myc.Ast.Types,
Myc.Ast.Identities, // Wichtig für Casts (AsNamed etc.)
Myc.Ast;
type
@@ -107,7 +108,6 @@ begin
begin
if not Assigned(ctx.FParent) then
begin
// Should technically not happen if Binder did its job, but safe guard it.
Result := TTypes.Unknown;
Exit;
end;
@@ -180,7 +180,7 @@ function TTypeChecker.Execute(const RootNode: IAstNode; const Layout: IScopeLayo
begin
Result := Accept(RootNode);
if not Assigned(Result) then
Result := TAst.Block([]);
Result := TAst.Block([], nil);
end;
function TTypeChecker.VisitConstant(const Node: IConstantNode): IAstNode;
@@ -197,18 +197,21 @@ function TTypeChecker.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
var
typ: IStaticType;
adr: TResolvedAddress;
identity: INamedIdentity;
begin
adr := Node.Address;
identity := Node.Identity.AsNamed; // Extract specific identity
// If binder failed to resolve, we propagate Unknown type to prevent cascading errors.
if adr.Kind = akUnresolved then
begin
Result := TAst.Identifier(Node.Name, adr, TTypes.Unknown);
// Propagate identity, set type to Unknown
Result := TAst.Identifier(identity, adr, TTypes.Unknown);
Exit;
end;
typ := FCurrentContext.LookupType(adr);
Result := TAst.Identifier(Node.Name, adr, typ);
// CoW: Reuse Identity
Result := TAst.Identifier(identity, adr, typ);
end;
function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode;
@@ -220,7 +223,8 @@ begin
for i := 0 to High(Node.Arguments) do
newArgs[i] := Accept(Node.Arguments[i]);
Result := TAst.Recur(newArgs, TTypes.Void);
// CoW: Reuse Identity
Result := TAst.Recur(Node.Identity, newArgs, TTypes.Void);
end;
function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
@@ -231,11 +235,14 @@ var
lambdaNode: ILambdaExpressionNode;
placeholderType: IStaticType;
i: Integer;
identNode: IIdentifierNode;
identIdentity: INamedIdentity;
begin
adr := Node.Target.AsIdentifier.Address;
identNode := Node.Target.AsIdentifier;
identIdentity := identNode.Identity.AsNamed;
adr := identNode.Address;
initType := TTypes.Unknown;
// If address is unresolved (binder error), we just process the initializer and return.
if adr.Kind = akUnresolved then
begin
if Assigned(Node.Initializer) then
@@ -271,8 +278,11 @@ begin
if initType.Kind <> stUnknown then
FCurrentContext.SetType(adr.SlotIndex, initType);
newIdent := TAst.Identifier(Node.Target.AsIdentifier.Name, adr, initType);
Result := TAst.VarDecl(newIdent.AsIdentifier, newInitializer, initType, Node.IsBoxed);
// Reuse Identity for Identifier
newIdent := TAst.Identifier(identIdentity, adr, initType);
// Reuse Identity for VarDecl
Result := TAst.VarDecl(Node.Identity, newIdent, newInitializer, initType, Node.IsBoxed);
end;
function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
@@ -283,12 +293,17 @@ var
lambdaNode: ILambdaExpressionNode;
placeholderType: IStaticType;
i: Integer;
identNode: IIdentifierNode;
identIdentity: INamedIdentity;
begin
newIdent := Accept(Node.Target);
targetType := newIdent.AsTypedNode.StaticType;
adr := newIdent.AsIdentifier.Address;
// Rebuild Identifier with potential type info updates
identNode := Node.Target.AsIdentifier;
identIdentity := identNode.Identity.AsNamed;
newIdent := Accept(Node.Target); // This visits identifier and might update type from context
targetType := newIdent.AsTypedNode.StaticType;
adr := identNode.Address;
// If binder failed, address is unresolved. We still check the value but skip assignment logic.
if adr.Kind = akUnresolved then
begin
Accept(Node.Value);
@@ -316,26 +331,25 @@ begin
newValue := Accept(Node.Value);
sourceType := newValue.AsTypedNode.StaticType;
// Only check assignment if both types are known to avoid cascading error reports
if (targetType.Kind <> stUnknown) and (sourceType.Kind <> stUnknown) then
begin
if not TTypeRules.CanAssign(targetType, sourceType) then
begin
if Assigned(FLog) then
FLog.AddError(Format('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]), Node);
// We continue, effectively ignoring the assignment type-wise (variable keeps old type).
end;
end;
// Type Inference for 'Unknown' targets
if ((targetType.Kind = stUnknown) or Assigned(placeholderType)) and (sourceType.Kind <> stUnknown) then
begin
FCurrentContext.SetType(adr.SlotIndex, sourceType);
newIdent := TAst.Identifier(newIdent.AsIdentifier.Name, adr, sourceType);
// Recreate Identifier with new type and original identity
newIdent := TAst.Identifier(identIdentity, adr, sourceType);
targetType := sourceType;
end;
Result := TAst.Assign(newIdent.AsIdentifier, newValue, targetType);
// CoW: Reuse Assignment Identity
Result := TAst.Assign(Node.Identity, newIdent, newValue, targetType);
end;
function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
@@ -348,6 +362,8 @@ var
upvalueAddrs: TArray<TResolvedAddress>;
i: Integer;
finalDescriptor: IScopeDescriptor;
paramIdent: IIdentifierNode;
paramIdentity: INamedIdentity;
begin
upvalueAddrs := Node.Upvalues;
SetLength(upvalueTypes, Length(upvalueAddrs));
@@ -361,23 +377,24 @@ begin
for i := 0 to High(Node.Parameters) do
begin
var paramIdent := Node.Parameters[i];
paramIdent := Node.Parameters[i];
paramIdentity := paramIdent.Identity.AsNamed;
var paramAdr := paramIdent.Address;
paramTypes[i] := TTypes.Unknown;
if paramAdr.Kind <> akUnresolved then
FCurrentContext.SetType(paramAdr.SlotIndex, paramTypes[i]);
newParams[i] := TAst.Identifier(paramIdent.Name, paramAdr, paramTypes[i]);
// CoW: Reuse Parameter Identity
newParams[i] := TAst.Identifier(paramIdentity, paramAdr, paramTypes[i]);
end;
newBody := Accept(Node.Body);
bodyType := newBody.AsTypedNode.StaticType;
methodType := TTypes.CreateMethod(paramTypes, bodyType);
// Set self-reference type (slot 0 is <self>)
FCurrentContext.SetType(0, methodType);
finalDescriptor := TScope.CreateDescriptor(Node.Layout, FCurrentContext.Types);
finally
var temp := FCurrentContext;
@@ -385,8 +402,19 @@ begin
temp.Free;
end;
// CoW: Reuse Lambda Identity
Result :=
TAst.LambdaExpr(newParams, newBody, Node.Layout, finalDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.IsPure, methodType);
TAst.LambdaExpr(
Node.Identity,
newParams,
newBody,
Node.Layout,
finalDescriptor,
Node.Upvalues,
Node.HasNestedLambdas,
Node.IsPure,
methodType
);
end;
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
@@ -449,7 +477,6 @@ begin
retType := bestSig.ReturnType
else
begin
// Log error, but don't crash
if Assigned(FLog) then
begin
argsStr := '';
@@ -460,15 +487,8 @@ begin
Node
);
end;
retType := TTypes.Unknown; // Poison result
retType := TTypes.Unknown;
end;
end
else
begin
// We have unknown arguments (probably due to upstream errors).
// We cannot resolve the signature, so the result is Unknown.
// We do NOT log an error here to avoid spam.
retType := TTypes.Unknown;
end;
end
else if calleeType.Kind <> TStaticTypeKind.stUnknown then
@@ -478,7 +498,17 @@ begin
retType := TTypes.Unknown;
end;
Result := TAst.FunctionCall(newCallee, newArgs, retType, Node.IsTailCall);
// CoW: Reuse Call Identity
Result :=
TAst.FunctionCall(
Node.Identity,
newCallee,
newArgs,
retType,
Node.IsTailCall,
nil, // StaticTarget set by Specializer
False // IsTargetPure set by Specializer
);
end;
function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
@@ -496,7 +526,8 @@ begin
else
blockType := TTypes.Void;
Result := TAst.Block(newExprs, blockType);
// CoW: Reuse Block Identity
Result := TAst.Block(Node.Identity, newExprs, blockType);
end;
function TTypeChecker.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
@@ -509,7 +540,6 @@ begin
newElse := Accept(Node.ElseBranch);
conditionType := newCond.AsTypedNode.StaticType;
if (conditionType.Kind <> stUnknown)
and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType)
and not TTypeRules.CanAssign(TTypes.Boolean, conditionType) then
@@ -531,7 +561,8 @@ begin
resultType := TTypes.Unknown;
end;
Result := TAst.IfExpr(newCond, newThen, newElse, resultType);
// CoW: Reuse If Identity
Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, resultType);
end;
function TTypeChecker.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
@@ -563,7 +594,8 @@ begin
resultType := TTypes.Unknown;
end;
Result := TAst.TernaryExpr(newCond, newThen, newElse, resultType);
// CoW: Reuse Ternary Identity
Result := TAst.TernaryExpr(Node.Identity, newCond, newThen, newElse, resultType);
end;
function TTypeChecker.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
@@ -616,7 +648,8 @@ begin
end;
end;
Result := TAst.MemberAccess(newBase, newMember.AsKeyword, elemType);
// CoW: Reuse MemberAccess Identity
Result := TAst.MemberAccess(Node.Identity, newBase, newMember.AsKeyword, elemType);
end;
function TTypeChecker.VisitIndexer(const Node: IIndexerNode): IAstNode;
@@ -653,7 +686,8 @@ begin
FLog.AddError(Format('Indexer `[]` requires an Ordinal index, but got %s', [indexType.ToString]), Node);
end;
Result := TAst.Indexer(newBase, newIndex, elemType);
// CoW: Reuse Indexer Identity
Result := TAst.Indexer(Node.Identity, newBase, newIndex, elemType);
end;
function TTypeChecker.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
@@ -705,7 +739,7 @@ begin
begin
def := TScalarRecordRegistry.Intern(scalarDefFields);
staticType := TTypes.CreateRecord(def);
Result := TAst.RecordLiteral(newFields, def, nil, staticType);
Result := TAst.RecordLiteral(Node.Identity, newFields, def, nil, staticType);
end
else
begin
@@ -716,16 +750,19 @@ begin
var genDef := TGenericRecordRegistry.Intern(genDefFields);
staticType := TTypes.CreateGenericRecord(genDef);
Result := TAst.RecordLiteral(newFields, nil, genDef, staticType);
Result := TAst.RecordLiteral(Node.Identity, newFields, nil, genDef, staticType);
end;
end;
function TTypeChecker.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
var
elemType: IStaticType;
defIdentity: IDefinitionIdentity;
begin
try
elemType := TTypes.FromScalarKind(TScalar.StringToKind(Node.Definition));
// Use definition from Identity to ensure SSOT
defIdentity := Node.Identity.AsDefinition;
elemType := TTypes.FromScalarKind(TScalar.StringToKind(defIdentity.Definition));
except
on E: Exception do
begin
@@ -735,7 +772,8 @@ begin
end;
end;
Result := TAst.CreateSeries(Node.Definition, TTypes.CreateSeries(elemType));
// CoW: Reuse Definition Identity
Result := TAst.CreateSeries(defIdentity, TTypes.CreateSeries(elemType));
end;
function TTypeChecker.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
@@ -759,7 +797,6 @@ begin
end
else
begin
// Only check assignment compatibility if value type is known
if (valueType.Kind <> stUnknown) and not TTypeRules.CanAssign(seriesType.ElementType, valueType) then
begin
if Assigned(FLog) then
@@ -781,12 +818,13 @@ begin
end;
end;
Result := TAst.AddSeriesItem(newSeries.AsIdentifier, newValue, newLookback, TTypes.Void);
// CoW: Reuse Add Identity
Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, TTypes.Void);
end;
function TTypeChecker.VisitNop(const Node: INopNode): IAstNode;
begin
Result := TAst.Nop(TTypes.Void);
Result := TAst.Nop(Node.Identity, TTypes.Void);
end;
function TTypeChecker.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
@@ -805,7 +843,8 @@ begin
FLog.AddError(Format('"length" requires a series, but got %s', [seriesType.ToString]), Node);
end;
Result := TAst.SeriesLength(newSeries.AsIdentifier, TTypes.Ordinal);
// CoW: Reuse SeriesLength Identity
Result := TAst.SeriesLength(Node.Identity, newSeries.AsIdentifier, TTypes.Ordinal);
end;
end.