Static specialization WIP
This commit is contained in:
@@ -36,8 +36,6 @@ type
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode; override;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode; override;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode; override;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
|
||||
function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
|
||||
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
|
||||
@@ -137,36 +135,60 @@ var
|
||||
initType: IStaticType;
|
||||
newInitializer, newIdent: IAstNode;
|
||||
adr: TResolvedAddress;
|
||||
lambdaNode: ILambdaExpressionNode;
|
||||
placeholderType: IStaticType;
|
||||
i: Integer;
|
||||
begin
|
||||
// 1. Visit Initializer first (if it exists)
|
||||
// 1. Get the address from the bound identifier (Binder did this)
|
||||
adr := Node.Identifier.Address;
|
||||
initType := TTypes.Unknown; // Default
|
||||
|
||||
// 2. Check for recursive lambda and bootstrap the type
|
||||
placeholderType := nil;
|
||||
if (Node.Initializer <> nil) and (Node.Initializer.Kind = akLambdaExpression) then
|
||||
begin
|
||||
lambdaNode := Node.Initializer.AsLambdaExpression;
|
||||
|
||||
// Create a placeholder method type based on the *unvisited* lambda's parameter *count*.
|
||||
var paramTypes: TArray<IStaticType>;
|
||||
SetLength(paramTypes, Length(lambdaNode.Parameters));
|
||||
for i := 0 to High(paramTypes) do
|
||||
paramTypes[i] := TTypes.Unknown;
|
||||
|
||||
// Create the placeholder (Return type is also Unknown for now)
|
||||
placeholderType := TTypes.CreateMethod(paramTypes, TTypes.Unknown);
|
||||
|
||||
// 3. *Update the descriptor* with the placeholder *before* visiting the initializer
|
||||
FCurrentDescriptor.UpdateType(adr.SlotIndex, placeholderType);
|
||||
initType := placeholderType; // Store this
|
||||
end;
|
||||
|
||||
// 4. Visit Initializer (if it exists)
|
||||
if Assigned(Node.Initializer) then
|
||||
newInitializer := Accept(Node.Initializer)
|
||||
else
|
||||
newInitializer := nil;
|
||||
|
||||
// 2. Get initializer type
|
||||
// 5. Get the *final* inferred initializer type
|
||||
if Assigned(newInitializer) then
|
||||
initType := newInitializer.AsTypedNode.StaticType
|
||||
else
|
||||
initType := TTypes.Unknown; // (def fib)
|
||||
else if not Assigned(placeholderType) then // only if not already set
|
||||
initType := TTypes.Unknown; // (def f) - no initializer
|
||||
|
||||
// 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).
|
||||
// 6. *Re-update* the type in the scope descriptor with the final, inferred type.
|
||||
if initType.Kind <> stUnknown then
|
||||
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
|
||||
|
||||
// 5. Create the new (typed) identifier node
|
||||
// 7. Create the new (typed) identifier node
|
||||
newIdent := TAst.Identifier(Node.Identifier.Name, adr, initType);
|
||||
|
||||
// 6. Create the new VariableDeclaration node using the factory
|
||||
// 8. Create the new VariableDeclaration node using the factory
|
||||
Result :=
|
||||
TAst.VarDecl(
|
||||
newIdent.AsIdentifier,
|
||||
newInitializer,
|
||||
initType,
|
||||
Node.IsBoxed // 7. Copy runtime flags (IsBoxed) via interface
|
||||
Node.IsBoxed // 9. Copy runtime flags
|
||||
);
|
||||
end;
|
||||
|
||||
@@ -175,32 +197,65 @@ var
|
||||
targetType, sourceType: IStaticType;
|
||||
newIdent, newValue: IAstNode;
|
||||
adr: TResolvedAddress;
|
||||
lambdaNode: ILambdaExpressionNode;
|
||||
placeholderType: IStaticType;
|
||||
i: Integer;
|
||||
begin
|
||||
// 1. Visit children first (Identifier, Value)
|
||||
newValue := Accept(Node.Value);
|
||||
// 1. Visit Identifier *first* to get its address and current type
|
||||
newIdent := Accept(Node.Identifier);
|
||||
|
||||
// 2. Get types
|
||||
targetType := newIdent.AsTypedNode.StaticType;
|
||||
adr := newIdent.AsIdentifier.Address;
|
||||
|
||||
// 2. Check for recursive lambda assignment
|
||||
placeholderType := nil;
|
||||
if (Node.Value <> nil) and (Node.Value.Kind = akLambdaExpression) then
|
||||
begin
|
||||
lambdaNode := Node.Value.AsLambdaExpression;
|
||||
|
||||
// Create a placeholder (only if the target is not already a method type)
|
||||
if (targetType.Kind <> stMethod) then
|
||||
begin
|
||||
var paramTypes: TArray<IStaticType>;
|
||||
SetLength(paramTypes, Length(lambdaNode.Parameters));
|
||||
for i := 0 to High(paramTypes) do
|
||||
paramTypes[i] := TTypes.Unknown; // We infer param types later
|
||||
|
||||
placeholderType := TTypes.CreateMethod(paramTypes, TTypes.Unknown);
|
||||
|
||||
// 3. *Update the descriptor* with the placeholder *before* visiting the value
|
||||
FCurrentDescriptor.UpdateType(adr.SlotIndex, placeholderType);
|
||||
targetType := placeholderType;
|
||||
end;
|
||||
end;
|
||||
|
||||
// 4. Visit Value
|
||||
newValue := Accept(Node.Value);
|
||||
sourceType := newValue.AsTypedNode.StaticType;
|
||||
|
||||
// 3. Check assignment
|
||||
// 5. Check assignment
|
||||
if not TTypeRules.CanAssign(targetType, sourceType) then
|
||||
raise ETypeException.CreateFmt('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]);
|
||||
|
||||
// 4. If the target was 'Unknown' (from 'def'), update the descriptor
|
||||
// with the new, inferred type. This enables recursion.
|
||||
if (targetType.Kind = stUnknown) and (sourceType.Kind <> stUnknown) then
|
||||
begin
|
||||
adr := newIdent.AsIdentifier.Address;
|
||||
FCurrentDescriptor.UpdateType(adr.SlotIndex, sourceType);
|
||||
// If target was unknown, try promoting
|
||||
if (targetType.Kind = stUnknown) then
|
||||
begin
|
||||
if not TTypeRules.CanAssign(sourceType, targetType) then // Check reverse
|
||||
raise ETypeException.CreateFmt('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]);
|
||||
end
|
||||
else
|
||||
raise ETypeException.CreateFmt('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]);
|
||||
end;
|
||||
|
||||
// 6. If the target was 'Unknown' or a 'Placeholder', update the descriptor
|
||||
// with the new, final inferred type.
|
||||
if ((targetType.Kind = stUnknown) or Assigned(placeholderType)) and (sourceType.Kind <> stUnknown) then
|
||||
begin
|
||||
FCurrentDescriptor.UpdateType(adr.SlotIndex, sourceType);
|
||||
// Re-create the identifier node *with the new type*
|
||||
newIdent := TAst.Identifier(newIdent.AsIdentifier.Name, adr, sourceType);
|
||||
targetType := sourceType;
|
||||
end;
|
||||
|
||||
// 5. Create the new Assignment node
|
||||
// 7. Create the new Assignment node
|
||||
Result := TAst.Assign(newIdent.AsIdentifier, newValue, targetType);
|
||||
end;
|
||||
|
||||
@@ -255,15 +310,28 @@ end;
|
||||
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
var
|
||||
calleeType, retType: IStaticType;
|
||||
i: Integer;
|
||||
i, j: Integer;
|
||||
newCallee: IAstNode;
|
||||
newArgs: TArray<IAstNode>;
|
||||
argTypes: TArray<IStaticType>;
|
||||
hasUnknownArgs: Boolean;
|
||||
bestSig: IMethodSignature;
|
||||
sig: IMethodSignature;
|
||||
match: Boolean;
|
||||
begin
|
||||
// 1. Visit children first (bottom-up)
|
||||
newCallee := Accept(Node.Callee);
|
||||
SetLength(newArgs, Length(Node.Arguments));
|
||||
SetLength(argTypes, Length(Node.Arguments));
|
||||
|
||||
hasUnknownArgs := False;
|
||||
for i := 0 to High(Node.Arguments) do
|
||||
begin
|
||||
newArgs[i] := Accept(Node.Arguments[i]);
|
||||
argTypes[i] := newArgs[i].AsTypedNode.StaticType;
|
||||
if argTypes[i].Kind = stUnknown then
|
||||
hasUnknownArgs := True;
|
||||
end;
|
||||
|
||||
// 2. Get callee type (now inferred)
|
||||
calleeType := newCallee.AsTypedNode.StaticType;
|
||||
@@ -272,24 +340,58 @@ begin
|
||||
// 3. Perform type checking
|
||||
if calleeType.Kind = TStaticTypeKind.stMethod then
|
||||
begin
|
||||
var signature := calleeType.Signature;
|
||||
if Length(newArgs) <> Length(signature.ParamTypes) then
|
||||
raise ETypeException.CreateFmt('Function expects %d arguments, but got %d', [Length(signature.ParamTypes), Length(newArgs)]);
|
||||
|
||||
retType := signature.ReturnType;
|
||||
|
||||
// Check argument types
|
||||
for i := 0 to High(newArgs) do
|
||||
// If any argument is Unknown, we cannot resolve overloads.
|
||||
// The return type remains Unknown.
|
||||
if not hasUnknownArgs then
|
||||
begin
|
||||
var argType := newArgs[i].AsTypedNode.StaticType;
|
||||
var paramType := signature.ParamTypes[i];
|
||||
if not TTypeRules.CanAssign(paramType, argType) then
|
||||
bestSig := nil;
|
||||
for sig in calleeType.Signatures do
|
||||
begin
|
||||
// Check 1: Argument count
|
||||
if Length(sig.ParamTypes) <> Length(argTypes) then
|
||||
continue;
|
||||
|
||||
// Check 2: Argument types (CanAssign)
|
||||
match := True;
|
||||
for j := 0 to High(argTypes) do
|
||||
begin
|
||||
if not TTypeRules.CanAssign(sig.ParamTypes[j], argTypes[j]) then
|
||||
begin
|
||||
match := False;
|
||||
break; // This signature doesn't match
|
||||
end;
|
||||
end;
|
||||
|
||||
// Check 3: Found first match
|
||||
if match then
|
||||
begin
|
||||
// This is the "dumb" checker logic: first match wins.
|
||||
// A "smarter" checker would find the *best* match.
|
||||
bestSig := sig;
|
||||
break;
|
||||
end;
|
||||
end; // for sig
|
||||
|
||||
// Check 4: Handle results
|
||||
if Assigned(bestSig) then
|
||||
begin
|
||||
retType := bestSig.ReturnType;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// No signature matched, even with known types. This is an error.
|
||||
var argsStr: string := '';
|
||||
for i := 0 to High(argTypes) do
|
||||
argsStr := argsStr + argTypes[i].ToString + ' ';
|
||||
raise ETypeException
|
||||
.CreateFmt('Cannot assign argument %d (type %s) to parameter (type %s)', [i, argType.ToString, paramType.ToString]);
|
||||
.CreateFmt('No matching signature for call with args (%s) found on method %s', [argsStr, calleeType.ToString]);
|
||||
end;
|
||||
end;
|
||||
// else: hasUnknownArgs is True, so retType remains Unknown (as set in step 2)
|
||||
end
|
||||
else if calleeType.Kind <> TStaticTypeKind.stUnknown then
|
||||
raise ETypeException.CreateFmt('Cannot invoke type %s as a function.', [calleeType.ToString]);
|
||||
// else: calleeType is Unknown (e.g. recursive call or unbound symbol), retType remains Unknown.
|
||||
|
||||
// 4. Create the new (typed) call node using the factory
|
||||
Result :=
|
||||
@@ -374,44 +476,6 @@ begin
|
||||
Result := TAst.TernaryExpr(newCond, newThen, newElse, resultType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode;
|
||||
var
|
||||
leftType, rightType, resultType: IStaticType;
|
||||
newLeft, newRight: IAstNode;
|
||||
begin
|
||||
// 1. Visit children
|
||||
newLeft := Accept(Node.Left);
|
||||
newRight := Accept(Node.Right);
|
||||
|
||||
// 2. Get types
|
||||
leftType := newLeft.AsTypedNode.StaticType;
|
||||
rightType := newRight.AsTypedNode.StaticType;
|
||||
|
||||
// 3. Resolve
|
||||
resultType := TTypeRules.ResolveBinaryOp(Node.Operator, leftType, rightType);
|
||||
|
||||
// 4. Create new node
|
||||
Result := TAst.BinaryExpr(newLeft, Node.Operator, newRight, resultType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode;
|
||||
var
|
||||
rightType, resultType: IStaticType;
|
||||
newRight: IAstNode;
|
||||
begin
|
||||
// 1. Visit children
|
||||
newRight := Accept(Node.Right);
|
||||
|
||||
// 2. Get types
|
||||
rightType := newRight.AsTypedNode.StaticType;
|
||||
|
||||
// 3. Resolve
|
||||
resultType := TTypeRules.ResolveUnaryOp(Node.Operator, rightType);
|
||||
|
||||
// 4. Create new node
|
||||
Result := TAst.UnaryExpr(Node.Operator, newRight, resultType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
|
||||
var
|
||||
baseType, elemType: IStaticType;
|
||||
|
||||
Reference in New Issue
Block a user