diff options
| author | _ov4 <ov4@ov4.org> | 2026-07-26 17:36:41 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-07-26 17:36:41 +0000 |
| commit | d65c296a0ad8d335aa076aab48a8b198fa934a2d (patch) | |
| tree | 80592a0faec64482a7a81d6af4836e52b4fd33ce /lib/CodeGen | |
| parent | f19f1c956780f41f1440b372187770ce0be8efed (diff) | |
feat: records with designator lvalues, and pointer types (CIE 4.1/4.2)
- New CodeGen::emitLValue resolves any designator โ variable, checked
array element, record field chain (CreateStructGEP via the record
payload), pointer deref (with the null-pointer [Fatal] check) โ to an
address + type name; loadFromLValue centralizes the rvalue read and the
STRING null->"" guard, so zero-initialized STRING fields read as "".
- Designator assignment stores through emitLValue + emitCoercedExpr;
whole-record copies (r2 <- r1, array elements, BYVAL args, RETURN) are
aggregate loads/stores with name-checked admission.
- getExprTypeInfo walks designator chains statically (field types from
the record payload, deref to the pointee), so records/pointers type
correctly in every expression context.
- Address-of ^x admits into a pointer target only when the designator's
type name equals the declared pointee exactly (nominal, strict: the
guide's own ^INTEGER-at-a-Season example is a compile error by design);
^arr[i] and ^rec.field work through the same lvalue path.
- Verified: the guide's ยง4.2 StudentRecord example (minus DATE), nested
records, arrays of records with Form[i].YearGroup writes, record
BYREF/BYVAL params, NodePtr = ^Node forward reference with writes
through n1.Next^.Data, unset-pointer deref dying with [Fatal].
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 60 | ||||
| -rw-r--r-- | lib/CodeGen/Designator.cc | 149 |
2 files changed, 198 insertions, 11 deletions
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index ae8efa2..62a6f1e 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -186,6 +186,41 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { return Info ? resolveType(Info->TypeName) : nullptr; } + if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) { + const SymbolInfo *Info = getSymbolInfo(D->getBaseName()); + if (!Info) return nullptr; + std::string TypeName = Info->TypeName; + size_t Start = 0; + if (Info->IsArray) { + // SymbolInfo already holds the ELEMENT type name for arrays. + if (D->getAccesses().empty() || D->getAccesses()[0].Kind != AccessKind::Index) + return nullptr; + Start = 1; + } + for (size_t I = Start; I < D->getAccesses().size(); ++I) { + const TypeInfo *Cur = resolveType(TypeName); + if (!Cur) return nullptr; + const DesignatorAccess &A = D->getAccesses()[I]; + if (A.Kind == AccessKind::Field) { + const RecordPayload *RP = Cur->asRecord(); + const RecordFieldInfo *FI = RP ? RP->findField(A.FieldName) : nullptr; + if (!FI) return nullptr; + TypeName = FI->TypeName; + } else if (A.Kind == AccessKind::Deref) { + const PointerPayload *PP = Cur->asPointer(); + if (!PP) return nullptr; + TypeName = PP->PointeeTypeName; + } else { + return nullptr; // indexing a non-array + } + } + return resolveType(TypeName); + } + + // ^x has no pseudocode type of its own; only the pointer admission in + // emitCoercedExpr may consume it. + if (dynamic_cast<AddrOfExprAST*>(Expr)) return nullptr; + if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { if (Unary->getOp() == tok_not) { return resolveType("BOOLEAN"); @@ -683,10 +718,18 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { return FuncGen->emitCallExpr(Call, Args); } - // TYPE-system stubs: parsed but not yet wired (phases C/D/E of the TYPE work). - if (dynamic_cast<DesignatorExprAST*>(Expr) || dynamic_cast<AddrOfExprAST*>(Expr)) { - reportError("Record field / pointer expressions are not implemented yet"); - return nullptr; + if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) { + LValueInfo LV; + if (!emitLValue(D, LV)) return nullptr; + return loadFromLValue(LV); + } + + // ^x produces a raw address; it is only admissible where a pointer type + // is expected, which emitCoercedExpr enforces by pointee name. + if (auto *Addr = dynamic_cast<AddrOfExprAST*>(Expr)) { + LValueInfo LV; + if (!emitLValue(Addr->getTarget(), LV)) return nullptr; + return LV.Addr; } return nullptr; @@ -1115,9 +1158,12 @@ void CodeGen::emitStmt(StmtAST *Stmt) { dynamic_cast<RecordTypeDeclAST*>(Stmt)) { return; } - // TYPE-system stub: wired in the record/lvalue phase. - if (dynamic_cast<DesignatorAssignStmtAST*>(Stmt)) { - reportError("Assignment to record fields / pointer targets is not implemented yet"); + if (auto *DA = dynamic_cast<DesignatorAssignStmtAST*>(Stmt)) { + LValueInfo LV; + if (!emitLValue(DA->getTarget(), LV)) return; + Value *Val = emitCoercedExpr(DA->getExpr(), resolveType(LV.TypeName)); + if (!Val) return; + Builder->CreateStore(Val, LV.Addr); return; } } diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc index 58898fe..a0ffe28 100644 --- a/lib/CodeGen/Designator.cc +++ b/lib/CodeGen/Designator.cc @@ -4,6 +4,7 @@ // These are CodeGen methods; the file is split out to keep the hub // dispatch-only. #include "cps/CodeGen.h" +#include "cps/ArrayHandler.h" #include "cps/TypeAST.h" #include "cps/Lexer.h" @@ -66,6 +67,132 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem } } +// Resolves a designator (or legacy variable/array-access node) to the address +// of its storage plus the type name stored there. Array indexing goes through +// ArrayHandler's checked pipeline; dereferences emit the null-pointer check. +bool CodeGen::emitLValue(ExprAST *E, LValueInfo &Out) { + if (auto *Var = dynamic_cast<VariableExprAST*>(E)) { + const SymbolInfo *Info = getSymbolInfo(Var->getName()); + if (!Info) { + if (Types->lookupEnumConstant(Var->getName())) { + reportError("Enum value %s is not an assignable location", Var->getName().c_str()); + } else { + reportError("Unknown variable name %s", Var->getName().c_str()); + } + return false; + } + if (Info->IsArray) { + reportError("Whole array %s is not an assignable location; index it", Var->getName().c_str()); + return false; + } + Out.Addr = Info->Storage; + Out.TypeName = Info->TypeName; + return true; + } + + if (auto *Acc = dynamic_cast<ArrayAccessExprAST*>(E)) { + Out.Addr = Arrays->emitElementAddress(Acc, *this, Out.TypeName); + return Out.Addr != nullptr; + } + + auto *D = dynamic_cast<DesignatorExprAST*>(E); + if (!D) { + reportError("Expression is not an assignable location"); + return false; + } + + const SymbolInfo *Info = getSymbolInfo(D->getBaseName()); + if (!Info) { + if (Types->lookupEnumConstant(D->getBaseName())) { + reportError("Enum value %s is not an assignable location", D->getBaseName().c_str()); + } else { + reportError("Unknown variable name %s", D->getBaseName().c_str()); + } + return false; + } + + const auto &Accesses = D->getAccesses(); + Value *Addr = Info->Storage; + std::string TypeName = Info->TypeName; + size_t Start = 0; + + if (Info->IsArray) { + if (Accesses.empty() || Accesses[0].Kind != AccessKind::Index) { + reportError("Array %s must be indexed before '.' or '^' access", D->getBaseName().c_str()); + return false; + } + Addr = Arrays->emitElementAddress(D->getBaseName(), Accesses[0].Indices, + D->getLine(), *this, TypeName); + if (!Addr) return false; + Start = 1; + } + + for (size_t I = Start; I < Accesses.size(); ++I) { + const DesignatorAccess &A = Accesses[I]; + const TypeInfo *Cur = resolveType(TypeName); + if (!Cur) { + reportError("Unknown type %s", TypeName.c_str()); + return false; + } + + switch (A.Kind) { + case AccessKind::Field: { + const RecordPayload *RP = Cur->asRecord(); + if (!RP) { + reportError("Type %s is not a record; '.%s' is invalid", + TypeName.c_str(), A.FieldName.c_str()); + return false; + } + const RecordFieldInfo *FI = RP->findField(A.FieldName); + if (!FI) { + reportError("Record type %s has no field %s", TypeName.c_str(), A.FieldName.c_str()); + return false; + } + Addr = Builder->CreateStructGEP(Cur->LLVMType, Addr, FI->Index, + TypeName + "." + A.FieldName); + TypeName = FI->TypeName; + break; + } + + case AccessKind::Deref: { + const PointerPayload *PP = Cur->asPointer(); + if (!PP) { + reportError("Cannot dereference a value of type %s ('^' needs a pointer)", + TypeName.c_str()); + return false; + } + Value *P = Builder->CreateLoad(PointerType::getUnqual(*TheContext), Addr, "deref_ptr"); + RuntimeChecker->emitNullDerefCheck(P, D->getLine()); + Addr = P; + TypeName = PP->PointeeTypeName; + break; + } + + case AccessKind::Index: + reportError("Cannot index a value of type %s (only declared arrays can be indexed)", + TypeName.c_str()); + return false; + } + } + + Out.Addr = Addr; + Out.TypeName = TypeName; + return true; +} + +Value *CodeGen::loadFromLValue(const LValueInfo &LV) { + const TypeInfo *Info = resolveType(LV.TypeName); + if (!Info || !Info->LLVMType) { + reportError("Unknown type %s", LV.TypeName.c_str()); + return nullptr; + } + Value *Val = Builder->CreateLoad(Info->LLVMType, LV.Addr); + if (Info->isString()) { + Val = emitStringNullGuard(Val); + } + return Val; +} + // The single name-level admission gate. Targets of a user kind demand exact // type-name equality (their representations are indistinguishable at the // LLVM level); builtin targets additionally reject user-kind sources, then @@ -76,10 +203,24 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) { const TypeInfo *Src = getExprTypeInfo(E); if (Target->isUserKind()) { - // ^x into a pointer target compares pointee names, not type names; - // wired in the pointer phase. - if (Target->isPointer() && dynamic_cast<AddrOfExprAST*>(E)) { - return emitExpr(E); + // ^x into a pointer target is structural: the pointee type name must + // match the pointer type's declared pointee exactly (strict โ the + // guide's own ^INTEGER-at-a-Season example is rejected by design). + if (auto *Addr = dynamic_cast<AddrOfExprAST*>(E)) { + const PointerPayload *PP = Target->asPointer(); + if (!PP) { + reportError("'^' (address-of) can only be used where a pointer type is expected (got %s)", + Target->Name.c_str()); + return nullptr; + } + LValueInfo LV; + if (!emitLValue(Addr->getTarget(), LV)) return nullptr; + if (LV.TypeName != PP->PointeeTypeName) { + reportError("%s is a pointer to %s and cannot point at a value of type %s", + Target->Name.c_str(), PP->PointeeTypeName.c_str(), LV.TypeName.c_str()); + return nullptr; + } + return LV.Addr; } if (!Src) { // Emit anyway: an inadmissible sub-expression (e.g. enum * 2) |
