diff options
| author | _ov4 <ov4@ov4.org> | 2026-07-26 17:32:24 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-07-26 17:32:24 +0000 |
| commit | f19f1c956780f41f1440b372187770ce0be8efed (patch) | |
| tree | fe0c2fe47a2cddb1bd8bd92206d47136bfcbfd37 /lib/CodeGen | |
| parent | 3366a2e0fae05d9e792169e74471c7282bf18681 (diff) | |
feat: user type registry and enumerated types (CIE 4.1/4.2)
- TypeSystem: TypeKinds Enum/Record/Pointer with per-kind payloads and
checked accessors; two-pass registration (names first, then definitions
in source order) so pointer TYPEs may forward-reference; enum-constant
table owned by TypeSystem so constants stay visible inside function
bodies across the Symbols save/clear/restore. Dead surface removed
(Custom kind, Printable/Builtin/ReferenceLike, ElementSize, hasType,
CodeGen::getNamedValue/getLLVMType).
- Enums are i64 with 0-based ordinals (constexpr kEnumOrdinalBase); all
type identity is by NAME. New emitCoercedExpr is the single admission
gate, replacing raw coercion at assignments, array stores, BYVAL args,
RETURN, IF/WHILE/REPEAT/FOR operands, file operands and builtin args;
coerceValueToType now hard-errors if a user-kind target reaches it.
- Binary ops with user-kind operands are intercepted ahead of
ArithmeticHandler ('&'-intercept precedent): same-enum comparisons,
enum +/- INTEGER (with a runtime range check, [Fatal] on overflow, the
only site that can manufacture an invalid ordinal), same-name pointer
=/<>; everything else is a compile error naming the operator.
- OUTPUT is an exhaustive switch over TypeKind (enum prints its ordinal;
record/pointer are compile errors, also covering whole-array OUTPUT);
INPUT's silent %lld fallback for unknown kinds is now an explicit error.
- Enum value names are globally reserved: collisions with DECLARE,
array names, parameters and function names are compile errors.
- RuntimeCheck: emitEnumRangeCheck + emitNullDerefCheck (used by the
pointer phase) following the emitIndexCheck recipe.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/ArrayHandler.cc | 14 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 205 | ||||
| -rw-r--r-- | lib/CodeGen/Designator.cc | 179 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 10 | ||||
| -rw-r--r-- | lib/CodeGen/RuntimeCheck.cc | 26 | ||||
| -rw-r--r-- | lib/CodeGen/TypeSystem.cc | 269 |
6 files changed, 564 insertions, 139 deletions
diff --git a/lib/CodeGen/ArrayHandler.cc b/lib/CodeGen/ArrayHandler.cc index 7977563..d21d91c 100644 --- a/lib/CodeGen/ArrayHandler.cc +++ b/lib/CodeGen/ArrayHandler.cc @@ -84,6 +84,10 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { CG.reportError("Unknown array element type %s", Stmt->getType().c_str()); return; } + if (Types.lookupEnumConstant(Name)) { + CG.reportError("'%s' is an enum value and cannot be declared as an array", Name.c_str()); + return; + } std::vector<Value*> Lows; std::vector<Value*> Highs; @@ -199,8 +203,7 @@ void ArrayHandler::emitArrayAssign(ArrayAssignStmtAST *Stmt, CodeGen &CG) { Stmt->getLine(), CG, ElemTypeName); if (!ElemPtr) return; - Value *Val = CG.emitExpr(Stmt->getExpr()); - Val = CG.coerceValueToType(Val, Types.resolve(ElemTypeName)); + Value *Val = CG.emitCoercedExpr(Stmt->getExpr(), Types.resolve(ElemTypeName)); if (!Val) return; Builder->CreateStore(Val, ElemPtr); @@ -256,6 +259,13 @@ bool ArrayHandler::tryEmitArrayOutput(ExprAST *Expr, CodeGen &CG) { return false; } + const TypeInfo *ElemInfo = Types.resolve(getMetadata(Name)->ElementTypeName); + if (ElemInfo && !ElemInfo->isOutputtable()) { + CG.reportError("Cannot OUTPUT array %s of type %s; output individual fields", + Name.c_str(), ElemInfo->Name.c_str()); + return true; // diagnosed + } + std::vector<Value*> Indices; if (IndexExprs && !emitCheckedIndices(Name, *IndexExprs, Line, false, CG, Indices)) { return true; // diagnosed diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 8940e63..ae8efa2 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -163,15 +163,6 @@ const TypeInfo *CodeGen::resolveType(const std::string &TypeName) const { return Types->resolve(TypeName); } -Type *CodeGen::getLLVMType(const std::string &TypeName) const { - return Types->getLLVMType(TypeName); -} - -Value *CodeGen::getNamedValue(const std::string &Name) const { - auto It = Symbols.find(Name); - return It == Symbols.end() ? nullptr : It->second.Storage; -} - const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { if (!Expr) return nullptr; @@ -183,7 +174,11 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) { const SymbolInfo *Info = getSymbolInfo(Var->getName()); - return Info ? resolveType(Info->TypeName) : nullptr; + if (Info) return resolveType(Info->TypeName); + if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) { + return resolveType(EC->TypeName); + } + return nullptr; } if (auto *Arr = dynamic_cast<ArrayAccessExprAST*>(Expr)) { @@ -217,6 +212,15 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { const TypeInfo *L = getExprTypeInfo(Bin->getLHS()); const TypeInfo *R = getExprTypeInfo(Bin->getRHS()); + // Enum arithmetic: E +/- INTEGER (enum on the left) yields the enum; + // every other user-kind combination has no type (emission rejects it). + if ((L && L->isUserKind()) || (R && R->isUserKind())) { + if (L && L->isEnum() && R && R->Kind == TypeKind::Integer && + (Bin->getOp() == '+' || Bin->getOp() == '-')) { + return L; + } + return nullptr; + } if ((L && L->Kind == TypeKind::Real) || (R && R->Kind == TypeKind::Real)) { return resolveType("REAL"); } @@ -327,12 +331,15 @@ Value *CodeGen::coerceValueToType(Value *Val, const TypeInfo *TargetInfo) { case TypeKind::Void: return nullptr; - case TypeKind::Custom: - if (Val->getType() == TargetInfo->LLVMType) return Val; - if (Val->getType()->isPointerTy() && TargetInfo->LLVMType->isPointerTy()) { - return Builder->CreateBitCast(Val, TargetInfo->LLVMType, "custom_ptr_cast"); - } - break; + // Enum/Record/Pointer identity lives in the type NAME; at this level + // an enum is a plain i64 and a pointer a plain ptr, so nothing can be + // checked here. emitCoercedExpr is the only legitimate door. + case TypeKind::Enum: + case TypeKind::Record: + case TypeKind::Pointer: + reportError("Internal: a %s value reached representation coercion without a name-level check", + TargetInfo->Name.c_str()); + return nullptr; } reportError("Cannot coerce value of LLVM type to target type %s", @@ -346,6 +353,11 @@ void CodeGen::emitDeclareStmt(DeclareStmtAST *Stmt) { reportError("Unknown type %s", Stmt->getType().c_str()); return; } + if (Types->lookupEnumConstant(Stmt->getName())) { + reportError("'%s' is an enum value and cannot be declared as a variable", + Stmt->getName().c_str()); + return; + } Function *TheFunction = Builder->GetInsertBlock()->getParent(); AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Info->LLVMType, Stmt->getName()); @@ -374,35 +386,42 @@ Value *CodeGen::emitStringNullGuard(Value *StrVal) { void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewline) { if (!Val || !Info) return; - if (!Info->Printable && Info->Kind == TypeKind::Custom) { - reportError("OUTPUT for custom type %s is not implemented yet", Info->Name.c_str()); - return; - } std::vector<Value*> Args; Value *Fmt = nullptr; - if (Info->isReal()) { + // Exhaustive over TypeKind (no default): the compiler warns when a new + // kind is added without deciding its OUTPUT story. + switch (Info->Kind) { + case TypeKind::Real: Fmt = AppendNewline ? PrintfFloatFormatStr : Builder->CreateGlobalStringPtr("%f", "fmt_flt_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(Val); - } else if (Info->isBoolean()) { + break; + + case TypeKind::Boolean: { Value *BoolVal = coerceValueToType(Val, resolveType("BOOLEAN")); Fmt = AppendNewline ? PrintfStringFormatStr : Builder->CreateGlobalStringPtr("%s", "fmt_str_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(Builder->CreateSelect(BoolVal, TrueStr, FalseStr)); - } else if (Info->isString()) { + break; + } + + case TypeKind::String: { Value *StrVal = emitStringNullGuard(Val); Fmt = AppendNewline ? PrintfStringFormatStr : Builder->CreateGlobalStringPtr("%s", "fmt_str_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(StrVal); - } else if (Info->isChar()) { + break; + } + + case TypeKind::Char: { Value *CharVal = coerceValueToType(Val, resolveType("CHAR")); Value *Promoted = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "char_for_printf"); Fmt = AppendNewline @@ -410,13 +429,32 @@ void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewli : Builder->CreateGlobalStringPtr("%c", "fmt_chr_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(Promoted); - } else { - Value *IntVal = coerceValueToType(Val, resolveType("INTEGER")); + break; + } + + case TypeKind::Integer: + case TypeKind::Enum: { // an enum prints its ordinal (user decision) + Value *IntVal = Info->isEnum() ? Val : coerceValueToType(Val, resolveType("INTEGER")); Fmt = AppendNewline ? PrintfFormatStr : Builder->CreateGlobalStringPtr("%lld", "fmt_int_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(IntVal); + break; + } + + case TypeKind::Record: + reportError("Cannot OUTPUT a value of record type %s; output its fields individually", + Info->Name.c_str()); + return; + + case TypeKind::Pointer: + reportError("Cannot OUTPUT a value of pointer type %s", Info->Name.c_str()); + return; + + case TypeKind::Void: + reportError("Cannot OUTPUT a VOID value"); + return; } Builder->CreateCall(PrintfFunc, Args); @@ -428,6 +466,9 @@ void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) { BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", F); Builder->SetInsertPoint(BB); + // User types first: prototypes may name them in parameters/returns. + runTypePrePass(Statements); + // Pre-register every top-level FUNCTION/PROCEDURE prototype so calls may // precede definitions. for (const auto &Stmt : Statements) { @@ -479,6 +520,11 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) { const SymbolInfo *Info = getSymbolInfo(Var->getName()); if (!Info) { + // Enum values are bare identifiers; collisions with variables are + // rejected at declaration time, so the order here cannot shadow. + if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) { + return ConstantInt::get(*TheContext, APInt(64, static_cast<uint64_t>(EC->Ordinal), true)); + } reportError("Unknown variable name %s", Var->getName().c_str()); return nullptr; } @@ -504,16 +550,23 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { } if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { - Value *Operand = emitExpr(Unary->getOperand()); - if (!Operand) return nullptr; - if (Unary->getOp() == tok_not) { - Operand = coerceValueToType(Operand, resolveType("BOOLEAN")); + Value *Operand = emitCoercedExpr(Unary->getOperand(), resolveType("BOOLEAN")); + if (!Operand) return nullptr; return Builder->CreateNot(Operand, "nottmp"); } + return nullptr; } if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) { + // Enum/Record/Pointer operands never reach ArithmeticHandler or the + // '&' path: their admissible operations are decided by name here. + const TypeInfo *LT = getExprTypeInfo(Bin->getLHS()); + const TypeInfo *RT = getExprTypeInfo(Bin->getRHS()); + if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) { + return emitUserKindBinaryOp(Bin, LT, RT); + } + Value *L = emitExpr(Bin->getLHS()); Value *R = emitExpr(Bin->getRHS()); if (!L || !R) return nullptr; @@ -535,41 +588,47 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { } } if (Name == "LENGTH") { - return StrHandler->emitLength(emitExpr(Call->getArgs()[0].get())); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); + if (!Str) return nullptr; + return StrHandler->emitLength(Str); } if (Name == "MID") { - Value *Str = emitExpr(Call->getArgs()[0].get()); - Value *Start = coerceValueToType(emitExpr(Call->getArgs()[1].get()), resolveType("INTEGER")); - Value *Len = coerceValueToType(emitExpr(Call->getArgs()[2].get()), resolveType("INTEGER")); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); + Value *Start = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); + Value *Len = emitCoercedExpr(Call->getArgs()[2].get(), resolveType("INTEGER")); if (!Str || !Start || !Len) return nullptr; return StrHandler->emitMid(Str, Start, Len); } if (Name == "RIGHT") { - Value *Str = emitExpr(Call->getArgs()[0].get()); - Value *Len = coerceValueToType(emitExpr(Call->getArgs()[1].get()), resolveType("INTEGER")); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); + Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); if (!Str || !Len) return nullptr; return StrHandler->emitRight(Str, Len); } if (Name == "LEFT") { - Value *Str = emitExpr(Call->getArgs()[0].get()); - Value *Len = coerceValueToType(emitExpr(Call->getArgs()[1].get()), resolveType("INTEGER")); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); + Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); if (!Str || !Len) return nullptr; return StrHandler->emitLeft(Str, Len); } if (Name == "LCASE") { - Value *Str = emitExpr(Call->getArgs()[0].get()); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrHandler->emitLCase(Str); } if (Name == "UCASE") { - Value *Str = emitExpr(Call->getArgs()[0].get()); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrHandler->emitUCase(Str); } if (Name == "ASC") { + const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get()); + if (ArgType && ArgType->isUserKind()) { + reportError("ASC expects a CHAR or STRING (got %s)", ArgType->Name.c_str()); + return nullptr; + } Value *ArgVal = emitExpr(Call->getArgs()[0].get()); if (!ArgVal) return nullptr; - const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get()); Value *CharVal = nullptr; if (ArgType && ArgType->isChar()) { CharVal = coerceValueToType(ArgVal, resolveType("CHAR")); @@ -580,17 +639,22 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { return coerceValueToType(AscVal, resolveType("INTEGER")); } if (Name == "CHR") { - Value *IntVal = coerceValueToType(emitExpr(Call->getArgs()[0].get()), resolveType("INTEGER")); + Value *IntVal = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("INTEGER")); if (!IntVal) return nullptr; Value *CharVal = Builder->CreateTrunc(IntVal, Type::getInt8Ty(*TheContext), "chr_val"); return coerceValueToType(CharVal, resolveType("STRING")); } if (Name == "IS_NUM") { - Value *Str = emitExpr(Call->getArgs()[0].get()); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrConvHandler->emitIsNum(Str); } if (Name == "NUM_TO_STR") { + const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get()); + if (ArgType && ArgType->isUserKind()) { + reportError("NUM_TO_STR expects a number (got %s)", ArgType->Name.c_str()); + return nullptr; + } Value *NumV = emitExpr(Call->getArgs()[0].get()); if (!NumV) return nullptr; bool IsReal = NumV->getType()->isDoubleTy(); @@ -600,7 +664,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { return StrConvHandler->emitNumToStr(NumV, IsReal); } if (Name == "STR_TO_NUM") { - Value *Str = emitExpr(Call->getArgs()[0].get()); + Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrConvHandler->emitStrToNum(Str, true); } @@ -609,8 +673,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { reportError("Cannot use a whole array as the EOF filename"); return nullptr; } - Value *FileName = coerceValueToType(emitExpr(Call->getArgs()[0].get()), - resolveType("STRING")); + Value *FileName = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!FileName) return nullptr; return Files->emitEof(FileName, Call->getLine()); } @@ -681,7 +744,7 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, } Out.push_back(Info->Storage); } else { - Value *V = coerceValueToType(emitExpr(ArgExpr), resolveType(TypeName)); + Value *V = emitCoercedExpr(ArgExpr, resolveType(TypeName)); if (!V) return false; Out.push_back(V); } @@ -690,11 +753,9 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, } void CodeGen::emitIfStmt(IfStmtAST *Stmt) { - Value *CondV = emitExpr(Stmt->getCond()); + Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); if (!CondV) return; - CondV = coerceValueToType(CondV, resolveType("BOOLEAN")); - Function *TheFunction = Builder->GetInsertBlock()->getParent(); BasicBlock *ThenBB = BasicBlock::Create(*TheContext, "then", TheFunction); BasicBlock *ElseBB = BasicBlock::Create(*TheContext, "else", TheFunction); @@ -723,9 +784,8 @@ void CodeGen::emitWhileStmt(WhileStmtAST *Stmt) { Builder->CreateBr(CondBB); Builder->SetInsertPoint(CondBB); - Value *CondV = emitExpr(Stmt->getCond()); + Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); if (!CondV) return; - CondV = coerceValueToType(CondV, resolveType("BOOLEAN")); Builder->CreateCondBr(CondV, LoopBB, AfterBB); @@ -753,9 +813,8 @@ void CodeGen::emitRepeatStmt(RepeatStmtAST *Stmt) { Builder->CreateBr(CondBB); Builder->SetInsertPoint(CondBB); - Value *CondV = emitExpr(Stmt->getCond()); + Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); if (!CondV) return; - CondV = coerceValueToType(CondV, resolveType("BOOLEAN")); Builder->CreateCondBr(CondV, AfterBB, LoopBB); @@ -776,7 +835,7 @@ void CodeGen::emitForStmt(ForStmtAST *Stmt) { return; } - Value *StartVal = coerceValueToType(emitExpr(Stmt->getStart()), resolveType("INTEGER")); + Value *StartVal = emitCoercedExpr(Stmt->getStart(), resolveType("INTEGER")); if (!StartVal) return; Value *Alloca = Symbol->Storage; @@ -785,7 +844,7 @@ void CodeGen::emitForStmt(ForStmtAST *Stmt) { // STEP is evaluated exactly once, before the loop. Value *StepVal = nullptr; if (Stmt->getStep()) { - StepVal = coerceValueToType(emitExpr(Stmt->getStep()), resolveType("INTEGER")); + StepVal = emitCoercedExpr(Stmt->getStep(), resolveType("INTEGER")); if (!StepVal) return; } else { StepVal = ConstantInt::get(*TheContext, APInt(64, 1)); @@ -800,7 +859,7 @@ void CodeGen::emitForStmt(ForStmtAST *Stmt) { Builder->SetInsertPoint(CondBB); Value *CurVar = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); - Value *EndVal = coerceValueToType(emitExpr(Stmt->getEnd()), resolveType("INTEGER")); + Value *EndVal = emitCoercedExpr(Stmt->getEnd(), resolveType("INTEGER")); if (!EndVal) return; // The loop direction depends on the STEP value at run time. @@ -865,12 +924,13 @@ void CodeGen::emitStmt(StmtAST *Stmt) { if (auto *Ret = dynamic_cast<ReturnStmtAST*>(Stmt)) { Value *RetVal = nullptr; if (Ret->getRetVal()) { - RetVal = emitExpr(Ret->getRetVal()); Function *F = Builder->GetInsertBlock()->getParent(); if (const FuncSig *Sig = FuncGen->getSignature(F->getName().str())) { - RetVal = coerceValueToType(RetVal, resolveType(Sig->ReturnTypeName)); - if (!RetVal) return; + RetVal = emitCoercedExpr(Ret->getRetVal(), resolveType(Sig->ReturnTypeName)); + } else { + RetVal = emitExpr(Ret->getRetVal()); } + if (!RetVal) return; } FuncGen->emitReturn(Ret, RetVal); return; @@ -892,9 +952,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) { return; } - Value *Val = emitExpr(Assign->getExpr()); - const TypeInfo *TargetType = resolveType(Info->TypeName); - Val = coerceValueToType(Val, TargetType); + Value *Val = emitCoercedExpr(Assign->getExpr(), resolveType(Info->TypeName)); if (!Val) return; Builder->CreateStore(Val, Info->Storage); @@ -943,10 +1001,14 @@ void CodeGen::emitStmt(StmtAST *Stmt) { Args.push_back(CharFmt); Args.push_back(Info->Storage); Builder->CreateCall(ScanfFunc, Args); - } else { + } else if (TypeInfo->Kind == TypeKind::Integer) { Args.push_back(ScanfFormatStr); Args.push_back(Info->Storage); Builder->CreateCall(ScanfFunc, Args); + } else { + // Enum/Record/Pointer and any future kind: scanf'ing raw bytes + // into typed storage would corrupt it — reject explicitly. + reportError("INPUT is not supported for a value of type %s", TypeInfo->Name.c_str()); } return; } @@ -974,7 +1036,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) { reportError("Cannot use a whole array as the OPENFILE filename"); return; } - Value *Name = coerceValueToType(emitExpr(Open->getFileName()), resolveType("STRING")); + Value *Name = emitCoercedExpr(Open->getFileName(), resolveType("STRING")); if (!Name) return; Files->emitOpen(Name, Open->getMode(), Open->getLine()); return; @@ -999,7 +1061,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) { reportError("Cannot use a whole array as the READFILE filename"); return; } - Value *Name = coerceValueToType(emitExpr(Read->getFileName()), resolveType("STRING")); + Value *Name = emitCoercedExpr(Read->getFileName(), resolveType("STRING")); if (!Name) return; Value *LineStr = Files->emitRead(Name, Read->getLine()); if (!LineStr) return; @@ -1012,8 +1074,8 @@ void CodeGen::emitStmt(StmtAST *Stmt) { reportError("Cannot use a whole array in WRITEFILE"); return; } - Value *Name = coerceValueToType(emitExpr(Write->getFileName()), resolveType("STRING")); - Value *Data = coerceValueToType(emitExpr(Write->getData()), resolveType("STRING")); + Value *Name = emitCoercedExpr(Write->getFileName(), resolveType("STRING")); + Value *Data = emitCoercedExpr(Write->getData(), resolveType("STRING")); if (!Name || !Data) return; Files->emitWrite(Name, Data, Write->getLine()); return; @@ -1024,7 +1086,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) { reportError("Cannot use a whole array as the CLOSEFILE filename"); return; } - Value *Name = coerceValueToType(emitExpr(Close->getFileName()), resolveType("STRING")); + Value *Name = emitCoercedExpr(Close->getFileName(), resolveType("STRING")); if (!Name) return; Files->emitClose(Name, Close->getLine()); return; @@ -1047,12 +1109,13 @@ void CodeGen::emitStmt(StmtAST *Stmt) { return; } - // TYPE-system stubs: parsed but not yet wired (phases C/D of the TYPE work). + // TYPE declarations are registered by compile()'s pre-pass (the parser + // guarantees they are top-level); nothing is emitted here. if (dynamic_cast<EnumTypeDeclAST*>(Stmt) || dynamic_cast<PointerTypeDeclAST*>(Stmt) || dynamic_cast<RecordTypeDeclAST*>(Stmt)) { - reportError("TYPE declarations are not implemented yet"); 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"); return; diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc new file mode 100644 index 0000000..58898fe --- /dev/null +++ b/lib/CodeGen/Designator.cc @@ -0,0 +1,179 @@ +// Type-name-level emission helpers for the user-defined TYPE system: +// the type pre-pass, the admission gate for assignments/arguments, and the +// binary-operator interception for Enum/Record/Pointer operands. +// These are CodeGen methods; the file is split out to keep the hub +// dispatch-only. +#include "cps/CodeGen.h" +#include "cps/TypeAST.h" +#include "cps/Lexer.h" + +using namespace llvm; +using namespace cps; + +namespace { + +// Operator spelling for diagnostics (raw ASCII or a keyword token). +std::string opToText(int Op) { + switch (Op) { + case tok_eq: return "="; + case tok_ne: return "<>"; + case tok_le: return "<="; + case tok_ge: return ">="; + case tok_and: return "AND"; + case tok_or: return "OR"; + case tok_div: return "DIV"; + case tok_mod: return "MOD"; + default: + if (Op >= 0) return std::string(1, static_cast<char>(Op)); + return "?"; + } +} + +} // namespace + +// Registers every top-level TYPE declaration before prototypes are emitted. +// Two passes so a pointer TYPE may reference a type declared later +// (TYPE NodePtr = ^Node before TYPE Node — required for linked structures); +// by-value record fields still require their record type to be complete, +// which pass B's source order enforces. +void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statements) { + std::string Err; + + for (const auto &S : Statements) { + bool Ok = true; + if (auto *E = dynamic_cast<EnumTypeDeclAST*>(S.get())) + Ok = Types->declareUserType(E->getName(), TypeKind::Enum, Err); + else if (auto *P = dynamic_cast<PointerTypeDeclAST*>(S.get())) + Ok = Types->declareUserType(P->getName(), TypeKind::Pointer, Err); + else if (auto *R = dynamic_cast<RecordTypeDeclAST*>(S.get())) + Ok = Types->declareUserType(R->getName(), TypeKind::Record, Err); + if (!Ok) reportError("%s", Err.c_str()); + } + + for (const auto &S : Statements) { + bool Ok = true; + if (auto *E = dynamic_cast<EnumTypeDeclAST*>(S.get())) { + Ok = Types->defineEnum(E->getName(), E->getValueNames(), Err); + } else if (auto *P = dynamic_cast<PointerTypeDeclAST*>(S.get())) { + Ok = Types->definePointer(P->getName(), P->getPointeeTypeName(), Err); + } else if (auto *R = dynamic_cast<RecordTypeDeclAST*>(S.get())) { + std::vector<std::pair<std::string, std::string>> Fields; + for (const RecordFieldDecl &F : R->getFields()) + Fields.emplace_back(F.Name, F.TypeName); + Ok = Types->defineRecord(R->getName(), Fields, Err); + } + if (!Ok) reportError("%s", Err.c_str()); + } +} + +// 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 +// keep the existing representation coercions unchanged. +Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) { + if (!E || !Target) return nullptr; + + 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); + } + if (!Src) { + // Emit anyway: an inadmissible sub-expression (e.g. enum * 2) + // then reports its own, more specific diagnostic. + if (emitExpr(E)) { + reportError("Cannot determine the type of the value used where %s is expected", + Target->Name.c_str()); + } + return nullptr; + } + if (Src->Name != Target->Name) { + reportError("Cannot use a value of type %s where %s is expected", + Src->Name.c_str(), Target->Name.c_str()); + return nullptr; + } + return emitExpr(E); // same nominal type: no conversion needed + } + + if (Src && Src->isUserKind()) { + reportError("Cannot use a value of type %s where %s is expected", + Src->Name.c_str(), Target->Name.c_str()); + return nullptr; + } + return coerceValueToType(emitExpr(E), Target); +} + +// Binary operators with at least one Enum/Record/Pointer operand. Decides +// admissibility by type name and emits directly; ArithmeticHandler stays +// ignorant of user kinds. +Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, const TypeInfo *RT) { + int Op = Bin->getOp(); + const char *LName = LT ? LT->Name.c_str() : "?"; + const char *RName = RT ? RT->Name.c_str() : "?"; + + if ((LT && LT->isRecord()) || (RT && RT->isRecord())) { + reportError("Operator %s cannot be applied to a record value (type %s)", + opToText(Op).c_str(), (LT && LT->isRecord()) ? LName : RName); + return nullptr; + } + + if ((LT && LT->isPointer()) || (RT && RT->isPointer())) { + bool SameName = LT && RT && LT->isPointer() && RT->isPointer() && LT->Name == RT->Name; + if (SameName && (Op == tok_eq || Op == tok_ne)) { + Value *L = emitExpr(Bin->getLHS()); + Value *R = emitExpr(Bin->getRHS()); + if (!L || !R) return nullptr; + return Op == tok_eq ? Builder->CreateICmpEQ(L, R, "ptr_eq") + : Builder->CreateICmpNE(L, R, "ptr_ne"); + } + reportError("Pointer values support only = and <> between the same pointer type (got %s %s %s)", + LName, opToText(Op).c_str(), RName); + return nullptr; + } + + // At least one enum operand from here on. + bool BothSameEnum = LT && RT && LT->isEnum() && RT->isEnum() && LT->Name == RT->Name; + + switch (Op) { + case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge: { + if (!BothSameEnum) break; + Value *L = emitExpr(Bin->getLHS()); + Value *R = emitExpr(Bin->getRHS()); + if (!L || !R) return nullptr; + switch (Op) { + case tok_eq: return Builder->CreateICmpEQ(L, R, "enum_eq"); + case tok_ne: return Builder->CreateICmpNE(L, R, "enum_ne"); + case '<': return Builder->CreateICmpSLT(L, R, "enum_lt"); + case '>': return Builder->CreateICmpSGT(L, R, "enum_gt"); + case tok_le: return Builder->CreateICmpSLE(L, R, "enum_le"); + case tok_ge: return Builder->CreateICmpSGE(L, R, "enum_ge"); + } + return nullptr; + } + + case '+': case '-': { + // The one legal arithmetic shape (guide §4.2): enum on the left, + // INTEGER on the right, result checked against the enum's range. + if (!(LT && LT->isEnum() && RT && RT->Kind == TypeKind::Integer)) break; + Value *L = emitExpr(Bin->getLHS()); + Value *R = emitExpr(Bin->getRHS()); + if (!L || !R) return nullptr; + Value *Res = Op == '+' ? Builder->CreateAdd(L, R, "enum_step") + : Builder->CreateSub(L, R, "enum_step"); + RuntimeChecker->emitEnumRangeCheck(Res, LT->asEnum()->ValueNames.size(), + LT->Name, Bin->getLine()); + return Res; + } + + default: + break; + } + + reportError("Operator %s cannot be applied to %s and %s " + "(enums allow comparison with the same enum type, and enum + or - INTEGER)", + opToText(Op).c_str(), LName, RName); + return nullptr; +} diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index dc238b5..cbaee75 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -34,6 +34,10 @@ Type *FunctionGen::getLLVMType(const std::string &TypeName) { void FunctionGen::createArgumentAllocas(Function *F, const std::vector<ParamDecl> &Params) { Function::arg_iterator AI = F->arg_begin(); for (const ParamDecl &P : Params) { + if (Types.lookupEnumConstant(P.Name)) { + fprintf(stderr, "Error: parameter '%s' collides with an enum value\n", P.Name.c_str()); + HadError = true; + } Value *ArgVal = &(*AI); ++AI; ArgVal->setName(P.Name); @@ -70,6 +74,12 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { HadError = true; return nullptr; } + if (Types.lookupEnumConstant(Proto->getName())) { + fprintf(stderr, "Error: '%s' is an enum value and cannot be a FUNCTION/PROCEDURE name\n", + Proto->getName().c_str()); + HadError = true; + return nullptr; + } FuncSig Sig; Sig.ReturnTypeName = Proto->getReturnType(); diff --git a/lib/CodeGen/RuntimeCheck.cc b/lib/CodeGen/RuntimeCheck.cc index c2ced87..e6d4bf8 100644 --- a/lib/CodeGen/RuntimeCheck.cc +++ b/lib/CodeGen/RuntimeCheck.cc @@ -1,4 +1,5 @@ #include "cps/RuntimeCheck.h" +#include "cps/TypeSystem.h" // kEnumOrdinalBase #include "llvm/IR/Function.h" #include "llvm/IR/Type.h" #include "llvm/IR/Constants.h" @@ -24,6 +25,8 @@ void RuntimeCheck::setupExternalFunctions() { DivZeroMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Division by zero\n", "err_div_zero", 0, &TheModule); OutOfBoundsMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Array index out of bounds\n", "err_bounds", 0, &TheModule); + EnumRangeMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Value out of range for enum %s\n", "err_enum_range", 0, &TheModule); + NullDerefMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Dereference of an unset pointer\n", "err_null_deref", 0, &TheModule); } void RuntimeCheck::emitErrorAndExit(Value *Condition, Value *Msg, int Line) { @@ -59,6 +62,27 @@ void RuntimeCheck::emitIndexCheck(Value *Index, Value *Lower, Value *Upper, int Value *TooLow = Builder.CreateICmpSLT(Index, Lower, "too_low"); Value *TooHigh = Builder.CreateICmpSGT(Index, Upper, "too_high"); Value *OutOfBounds = Builder.CreateOr(TooLow, TooHigh, "out_of_bounds"); - + emitErrorAndExit(OutOfBounds, OutOfBoundsMsg, Line); } + +void RuntimeCheck::emitEnumRangeCheck(Value *Ordinal, uint64_t ValueCount, + const std::string &TypeName, int Line) { + Value *Lo = ConstantInt::get(TheContext, APInt(64, static_cast<uint64_t>(kEnumOrdinalBase), true)); + Value *Hi = ConstantInt::get(TheContext, + APInt(64, static_cast<uint64_t>(kEnumOrdinalBase) + ValueCount, true)); + Value *TooLow = Builder.CreateICmpSLT(Ordinal, Lo, "enum_too_low"); + Value *TooHigh = Builder.CreateICmpSGE(Ordinal, Hi, "enum_too_high"); + Value *OutOfRange = Builder.CreateOr(TooLow, TooHigh, "enum_out_of_range"); + + Value *NameStr = Builder.CreateGlobalStringPtr(TypeName, "enum_name", 0, &TheModule); + emitErrorAndExit(OutOfRange, EnumRangeMsg, + {ConstantInt::get(TheContext, APInt(32, Line)), NameStr}); +} + +void RuntimeCheck::emitNullDerefCheck(Value *Ptr, int Line) { + Value *IsNull = Builder.CreateICmpEQ(Ptr, + ConstantPointerNull::get(PointerType::getUnqual(TheContext)), + "ptr_is_null"); + emitErrorAndExit(IsNull, NullDerefMsg, Line); +} diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc index 830ce25..c170be8 100644 --- a/lib/CodeGen/TypeSystem.cc +++ b/lib/CodeGen/TypeSystem.cc @@ -5,6 +5,13 @@ using namespace llvm; using namespace cps; +const RecordFieldInfo *RecordPayload::findField(const std::string &Name) const { + for (const auto &F : Fields) { + if (F.first == Name) return &F.second; + } + return nullptr; +} + bool TypeInfo::isNumeric() const { return Kind == TypeKind::Integer || Kind == TypeKind::Real; } @@ -33,58 +40,62 @@ bool TypeInfo::isVoid() const { return Kind == TypeKind::Void; } -TypeSystem::TypeSystem(LLVMContext &Ctx) : Context(Ctx) { - registerBuiltins(); +bool TypeInfo::isEnum() const { + return Kind == TypeKind::Enum; } -void TypeSystem::registerBuiltins() { - registerType("INTEGER", - Type::getInt64Ty(Context), - 8, - TypeKind::Integer, - true, - false, - true); +bool TypeInfo::isRecord() const { + return Kind == TypeKind::Record; +} - registerType("REAL", - Type::getDoubleTy(Context), - 8, - TypeKind::Real, - true, - false, - true); +bool TypeInfo::isPointer() const { + return Kind == TypeKind::Pointer; +} - registerType("BOOLEAN", - Type::getInt1Ty(Context), - 1, - TypeKind::Boolean, - true, - false, - true); +bool TypeInfo::isUserKind() const { + return Kind == TypeKind::Enum || Kind == TypeKind::Record || Kind == TypeKind::Pointer; +} - registerType("STRING", - PointerType::getUnqual(Context), - 8, - TypeKind::String, - true, - true, - true); +bool TypeInfo::isOutputtable() const { + switch (Kind) { + case TypeKind::Integer: + case TypeKind::Real: + case TypeKind::Boolean: + case TypeKind::String: + case TypeKind::Char: + case TypeKind::Enum: // prints its ordinal + return true; + case TypeKind::Void: + case TypeKind::Record: + case TypeKind::Pointer: + return false; + } + return false; +} - registerType("CHAR", - Type::getInt8Ty(Context), - 1, - TypeKind::Char, - true, - false, - true); +const RecordPayload *TypeInfo::asRecord() const { + return Kind == TypeKind::Record ? &Record : nullptr; +} - registerType("VOID", - Type::getVoidTy(Context), - 0, - TypeKind::Void, - false, - false, - true); +const EnumPayload *TypeInfo::asEnum() const { + return Kind == TypeKind::Enum ? &Enum : nullptr; +} + +const PointerPayload *TypeInfo::asPointer() const { + return Kind == TypeKind::Pointer ? &Pointer : nullptr; +} + +TypeSystem::TypeSystem(LLVMContext &Ctx) : Context(Ctx) { + registerBuiltins(); +} + +void TypeSystem::registerBuiltins() { + registerType("INTEGER", Type::getInt64Ty(Context), TypeKind::Integer); + registerType("REAL", Type::getDoubleTy(Context), TypeKind::Real); + registerType("BOOLEAN", Type::getInt1Ty(Context), TypeKind::Boolean); + registerType("STRING", PointerType::getUnqual(Context), TypeKind::String); + registerType("CHAR", Type::getInt8Ty(Context), TypeKind::Char); + registerType("VOID", Type::getVoidTy(Context), TypeKind::Void); } const TypeInfo *TypeSystem::resolve(const std::string &TypeName) const { @@ -95,38 +106,162 @@ const TypeInfo *TypeSystem::resolve(const std::string &TypeName) const { return &It->second; } -bool TypeSystem::hasType(const std::string &TypeName) const { - return resolve(TypeName) != nullptr; -} - const TypeInfo *TypeSystem::registerType(const std::string &TypeName, Type *LLVMType, - uint64_t ElementSize, - TypeKind Kind, - bool Printable, - bool ReferenceLike, - bool Builtin) { + TypeKind Kind) { TypeInfo Info; Info.Name = TypeName; Info.LLVMType = LLVMType; - Info.ElementSize = ElementSize; Info.Kind = Kind; - Info.Printable = Printable; - Info.Builtin = Builtin; - Info.ReferenceLike = ReferenceLike; auto Result = Types.insert_or_assign(TypeName, std::move(Info)); return &Result.first->second; } -Type *TypeSystem::getLLVMType(const std::string &TypeName) const { - const TypeInfo *Info = resolve(TypeName); - return Info ? Info->LLVMType : nullptr; +bool TypeSystem::declareUserType(const std::string &Name, TypeKind Kind, std::string &Err) { + if (Types.count(Name)) { + Err = "Type " + Name + " is already defined"; + return false; + } + if (EnumConstants.count(Name)) { + Err = "'" + Name + "' is an enum value and cannot name a type"; + return false; + } + + Type *LLVMType = nullptr; + switch (Kind) { + case TypeKind::Enum: + LLVMType = Type::getInt64Ty(Context); // INTEGER representation; identity is the name + break; + case TypeKind::Pointer: + LLVMType = PointerType::getUnqual(Context); + break; + case TypeKind::Record: + // Opaque named struct; the body is set in pass B. + LLVMType = StructType::create(Context, std::string(kRecordStructPrefix) + Name); + break; + default: + Err = "Internal: declareUserType called with a builtin kind"; + return false; + } + + registerType(Name, LLVMType, Kind); + return true; +} + +bool TypeSystem::defineEnum(const std::string &Name, + const std::vector<std::string> &Values, + std::string &Err) { + auto It = Types.find(Name); + if (It == Types.end() || It->second.Kind != TypeKind::Enum) { + Err = "Internal: defineEnum on undeclared enum " + Name; + return false; + } + + for (size_t I = 0; I < Values.size(); ++I) { + const std::string &V = Values[I]; + for (size_t J = 0; J < I; ++J) { + if (Values[J] == V) { + Err = "Duplicate value '" + V + "' in enum " + Name; + return false; + } + } + if (Types.count(V)) { + Err = "Enum value '" + V + "' in " + Name + " collides with a type name"; + return false; + } + if (auto CIt = EnumConstants.find(V); CIt != EnumConstants.end()) { + Err = "Enum value '" + V + "' in " + Name + " is already a value of enum " + + CIt->second.TypeName; + return false; + } + } + + It->second.Enum.ValueNames = Values; + for (size_t I = 0; I < Values.size(); ++I) { + EnumConstants[Values[I]] = {Name, static_cast<int64_t>(I) + kEnumOrdinalBase}; + } + return true; +} + +bool TypeSystem::defineRecord(const std::string &Name, + const std::vector<std::pair<std::string, std::string>> &Fields, + std::string &Err) { + auto It = Types.find(Name); + if (It == Types.end() || It->second.Kind != TypeKind::Record) { + Err = "Internal: defineRecord on undeclared record " + Name; + return false; + } + + std::vector<Type*> FieldTypes; + RecordPayload Payload; + for (const auto &[FieldName, FieldTypeName] : Fields) { + if (Payload.findField(FieldName)) { + Err = "Duplicate field '" + FieldName + "' in record " + Name; + return false; + } + const TypeInfo *FieldInfo = resolve(FieldTypeName); + if (!FieldInfo) { + Err = "Unknown type " + FieldTypeName + " for field " + FieldName + + " of record " + Name; + return false; + } + if (FieldInfo->isVoid()) { + Err = "Field " + FieldName + " of record " + Name + " cannot be VOID"; + return false; + } + // By-value record fields require a *completed* record (source order); + // this also rules out self/mutual inclusion. Inclusion via a pointer + // type is fine (opaque ptr, always complete). + if (FieldInfo->isRecord() && llvm::cast<StructType>(FieldInfo->LLVMType)->isOpaque()) { + Err = "Record type " + FieldTypeName + " must be fully defined before field " + + FieldName + " of record " + Name + " (records cannot contain themselves)"; + return false; + } + + RecordFieldInfo FI; + FI.TypeName = FieldInfo->Name; + FI.Index = static_cast<unsigned>(FieldTypes.size()); + Payload.Fields.emplace_back(FieldName, FI); + FieldTypes.push_back(FieldInfo->LLVMType); + } + + llvm::cast<StructType>(It->second.LLVMType)->setBody(FieldTypes); + It->second.Record = std::move(Payload); + return true; +} + +bool TypeSystem::definePointer(const std::string &Name, + const std::string &PointeeName, + std::string &Err) { + auto It = Types.find(Name); + if (It == Types.end() || It->second.Kind != TypeKind::Pointer) { + Err = "Internal: definePointer on undeclared pointer type " + Name; + return false; + } + + const TypeInfo *Pointee = resolve(PointeeName); + if (!Pointee) { + Err = "Unknown pointee type " + PointeeName + " in pointer type " + Name; + return false; + } + if (Pointee->isVoid()) { + Err = "Pointer type " + Name + " cannot point to VOID"; + return false; + } + + It->second.Pointer.PointeeTypeName = Pointee->Name; + return true; +} + +const EnumConstant *TypeSystem::lookupEnumConstant(const std::string &Name) const { + auto It = EnumConstants.find(Name); + return It == EnumConstants.end() ? nullptr : &It->second; } -uint64_t TypeSystem::getElementSize(const std::string &TypeName) const { +Type *TypeSystem::getLLVMType(const std::string &TypeName) const { const TypeInfo *Info = resolve(TypeName); - return Info ? Info->ElementSize : 0; + return Info ? Info->LLVMType : nullptr; } Constant *TypeSystem::getSizeOfConstant(Type *Ty) const { @@ -152,8 +287,12 @@ Constant *TypeSystem::getZeroValue(const std::string &TypeName) const { return ConstantPointerNull::get(cast<PointerType>(Info->LLVMType)); case TypeKind::Void: return nullptr; - case TypeKind::Custom: - return Constant::getNullValue(Info->LLVMType); + case TypeKind::Enum: + return ConstantInt::get(Context, APInt(64, kEnumOrdinalBase)); + case TypeKind::Record: + return Constant::getNullValue(Info->LLVMType); // zeroinitializer + case TypeKind::Pointer: + return ConstantPointerNull::get(cast<PointerType>(Info->LLVMType)); } return Constant::getNullValue(Info->LLVMType); |
