Compiler exceptions

This commit is contained in:
Michael Schimmel
2025-11-25 15:51:04 +01:00
parent 4e508d90a5
commit 0a1df4e9fe
9 changed files with 194 additions and 303 deletions
+10 -67
View File
@@ -25,13 +25,12 @@ type
TTypeChecker = class(TAstTransformer, IAstTypeChecker)
private
type
// Helper to track types per scope during traversal (The "Scratchpad")
TTypeContext = class
private
FParent: TTypeContext;
FLayout: IScopeLayout;
FSlotTypes: TArray<IStaticType>;
FUpvalueTypes: TArray<IStaticType>; // Types of captured variables
FUpvalueTypes: TArray<IStaticType>;
public
constructor Create(AParent: TTypeContext; ALayout: IScopeLayout; const AUpvalueTypes: TArray<IStaticType>);
function LookupType(const Address: TResolvedAddress): IStaticType;
@@ -41,12 +40,9 @@ type
private
FCurrentContext: TTypeContext;
// Helper to recursively rebuild the context stack from the layout hierarchy
function CreateContextChain(L: IScopeLayout): TTypeContext;
protected
// Override all visit methods to perform type checking
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
@@ -64,7 +60,6 @@ type
function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
function VisitNop(const Node: INopNode): IAstNode; override;
// Base cases (types are now set here)
function VisitConstant(const Node: IConstantNode): IAstNode; override;
function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
@@ -92,7 +87,6 @@ begin
FLayout := ALayout;
FUpvalueTypes := AUpvalueTypes;
// Initialize slot types with Unknown
if Assigned(FLayout) then
begin
SetLength(FSlotTypes, FLayout.SlotCount);
@@ -125,7 +119,6 @@ begin
end;
akUpvalue:
begin
// Look up type in our local upvalue type cache
if (Address.SlotIndex >= 0) and (Address.SlotIndex < Length(FUpvalueTypes)) then
Result := FUpvalueTypes[Address.SlotIndex]
else
@@ -151,30 +144,20 @@ begin
if L = nil then
exit(nil);
// Recursively build parent context
p := CreateContextChain(L.Parent);
// Create context for current layout level
// Note: We don't know UpvalueTypes for these static/parent layouts here, so [] is passed.
// Also: Slot types will be initialized to Unknown.
Result := TTypeContext.Create(p, L, []);
end;
constructor TTypeChecker.Create(const RootLayout: IScopeLayout);
begin
inherited Create;
// Build the full context chain based on the Layout's parent hierarchy
// This ensures that ScopeDepth lookups can traverse up to the root.
FCurrentContext := CreateContextChain(RootLayout);
// Fallback if RootLayout is nil (should not happen in valid pipeline)
if FCurrentContext = nil then
FCurrentContext := TTypeContext.Create(nil, nil, []);
end;
destructor TTypeChecker.Destroy;
begin
// Cleanup context stack
while Assigned(FCurrentContext) do
begin
var temp := FCurrentContext;
@@ -192,7 +175,6 @@ end;
function TTypeChecker.Execute(const RootNode: IAstNode; const Layout: IScopeLayout): IAstNode;
begin
// Note: Layout passed here matches what was passed to Create/Constructor (via recursed ContextChain)
Result := Accept(RootNode);
if not Assigned(Result) then
Result := TAst.Block([]);
@@ -216,10 +198,7 @@ var
adr: TResolvedAddress;
begin
adr := Node.Address;
// Lookup type in our scratchpad context using the address resolved by Binder
typ := FCurrentContext.LookupType(adr);
// Create a new node with the inferred type
Result := TAst.Identifier(Node.Name, adr, typ);
end;
@@ -247,7 +226,6 @@ begin
adr := Node.Target.AsIdentifier.Address;
initType := TTypes.Unknown;
// Recursive lambda bootstrap logic
placeholderType := nil;
if (Node.Initializer <> nil) and (Node.Initializer.Kind = akLambdaExpression) then
begin
@@ -258,8 +236,6 @@ begin
paramTypes[i] := TTypes.Unknown;
placeholderType := TTypes.CreateMethod(paramTypes, TTypes.Unknown);
// Update Context (Scratchpad)
FCurrentContext.SetType(adr.SlotIndex, placeholderType);
initType := placeholderType;
end;
@@ -274,12 +250,10 @@ begin
else if not Assigned(placeholderType) then
initType := TTypes.Unknown;
// Update Context with final type
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);
end;
@@ -296,7 +270,6 @@ begin
targetType := newIdent.AsTypedNode.StaticType;
adr := newIdent.AsIdentifier.Address;
// Recursive lambda assignment check
placeholderType := nil;
if (Node.Value <> nil) and (Node.Value.Kind = akLambdaExpression) then
begin
@@ -344,28 +317,18 @@ var
newBody: IAstNode;
bodyType, methodType: IStaticType;
paramTypes: TArray<IStaticType>;
// Upvalue Type Resolution
upvalueTypes: TArray<IStaticType>;
upvalueAddrs: TArray<TResolvedAddress>;
i: Integer;
finalDescriptor: IScopeDescriptor;
begin
// 1. Resolve Upvalue Types *in the current (parent) context*
upvalueAddrs := Node.Upvalues;
SetLength(upvalueTypes, Length(upvalueAddrs));
for i := 0 to High(upvalueAddrs) do
begin
// We look up the physical address (from Binder) in the current context chain
upvalueTypes[i] := FCurrentContext.LookupType(upvalueAddrs[i]);
end;
// 2. Create new TypeContext for this lambda scope, PASSING the upvalue types
// We use the layout from the Binder-Result-Node
FCurrentContext := TTypeContext.Create(FCurrentContext, Node.Layout, upvalueTypes);
try
// 3. Set parameter types in the context
SetLength(newParams, Length(Node.Parameters));
SetLength(paramTypes, Length(Node.Parameters));
@@ -373,37 +336,22 @@ begin
begin
var paramIdent := Node.Parameters[i];
var paramAdr := paramIdent.Address;
// Here we could infer types if we had type annotations. For now, parameters are Unknown.
paramTypes[i] := TTypes.Unknown;
// Update context so body can resolve params
FCurrentContext.SetType(paramAdr.SlotIndex, paramTypes[i]);
newParams[i] := TAst.Identifier(paramIdent.Name, paramAdr, paramTypes[i]);
end;
// 4. Visit body
newBody := Accept(Node.Body);
bodyType := newBody.AsTypedNode.StaticType;
// 5. Create method type
methodType := TTypes.CreateMethod(paramTypes, bodyType);
// 6. Update <self> type in context (Slot 0)
FCurrentContext.SetType(0, methodType);
// 7. FINALIZE: Bake the Descriptor
finalDescriptor := TScope.CreateDescriptor(Node.Layout, FCurrentContext.Types);
finally
// 8. Pop Context
var temp := FCurrentContext;
FCurrentContext := FCurrentContext.FParent;
temp.Free;
end;
// 9. Create new node with the Descriptor attached!
Result :=
TAst.LambdaExpr(newParams, newBody, Node.Layout, finalDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.IsPure, methodType);
end;
@@ -509,7 +457,6 @@ begin
newElse := Accept(Node.ElseBranch);
conditionType := newCond.AsTypedNode.StaticType;
// Ordinal and Boolean are valid for if-conditions
if (conditionType.Kind <> stUnknown)
and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType)
and not TTypeRules.CanAssign(TTypes.Boolean, conditionType) then
@@ -520,12 +467,9 @@ begin
if newElse <> nil then newElse.AsTypedNode.StaticType
else TTypes.Void;
try
resultType := TTypeRules.Promote(thenType, elseType);
except
on E: Exception do // Catch base exception (ETypeException from Myc.Ast.Types)
raise ETypeCheckException.Create(E.Message);
end;
resultType := TTypeRules.Promote(thenType, elseType);
if resultType = nil then
raise ETypeCheckException.CreateFmt('Cannot promote types %s and %s in If-Expression', [thenType.ToString, elseType.ToString]);
Result := TAst.IfExpr(newCond, newThen, newElse, resultType);
end;
@@ -548,12 +492,9 @@ begin
thenType := newThen.AsTypedNode.StaticType;
elseType := newElse.AsTypedNode.StaticType;
try
resultType := TTypeRules.Promote(thenType, elseType);
except
on E: Exception do
raise ETypeCheckException.Create(E.Message);
end;
resultType := TTypeRules.Promote(thenType, elseType);
if resultType = nil then
raise ETypeCheckException.CreateFmt('Cannot promote types %s and %s in Ternary-Expression', [thenType.ToString, elseType.ToString]);
Result := TAst.TernaryExpr(newCond, newThen, newElse, resultType);
end;
@@ -698,10 +639,12 @@ var
elemType: IStaticType;
begin
try
// TScalar.StringToKind might throw EConvertError or generic Exception if string is invalid
elemType := TTypes.FromScalarKind(TScalar.StringToKind(Node.Definition));
except
on E: Exception do
elemType := TTypes.Unknown;
// Instead of silently using Unknown, we now raise a proper compiler error
raise ETypeCheckException.CreateFmt('Invalid type definition "%s" in new-series.', [Node.Definition]);
end;
Result := TAst.CreateSeries(Node.Definition, TTypes.CreateSeries(elemType));