From f14b50d776ec743bd62fda22a9c154bba9588840 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Thu, 22 Oct 2026 15:07:27 +0000 Subject: feat(codegen)!: reject implicit BOOLEAN, CHAR and INTEGER mixing Arithmetic and unary minus accept only INTEGER and REAL, AND/OR/NOT need BOOLEAN, comparisons need equal types (or INTEGER with REAL), and only INTEGER converts implicitly to REAL. Use ASC and CHR for characters. No existing test program relied on the now-illegal mixing, so none changed. Co-Authored-By: Claude Sonnet 5.5 --- lib/CodeGen/ExpressionGen.cc | 27 +++++++++++++++++++++++++++ lib/CodeGen/TypeCheckGen.cc | 15 +++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'lib') diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc index d7a4ba7..0434348 100644 --- a/lib/CodeGen/ExpressionGen.cc +++ b/lib/CodeGen/ExpressionGen.cc @@ -106,6 +106,10 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { reportError("Unary '-' requires a supported numeric operand"); return nullptr; } + if (Info->Kind == TypeKind::Boolean || Info->Kind == TypeKind::Char) { + reportError("Unary '-' cannot be applied to %s", Info->Name.c_str()); + return nullptr; + } Value *Operand = emitExpr(Unary->getOperand()); if (!Operand) return nullptr; return ArithHandler->emitBinaryOp('-', ConstantInt::get(Type::getInt64Ty(*TheContext), 0), @@ -188,6 +192,29 @@ Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT, return emitCBinaryOp(Op, L, LT, R, RT, Line); if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) return emitUserKindBinaryOp(Op, L, LT, R, RT, Line); + + // Plain scalars only: BOOLEAN and CHAR are not numbers (use ASC / CHR), + // AND / OR take BOOLEAN, comparisons need equal types or INTEGER with REAL. + auto Numeric = [](const TypeInfo *T) { + return T->Kind == TypeKind::Integer || T->Kind == TypeKind::Real; + }; + bool Ok = true; + switch (Op) { + case '+': case '-': case '*': case '/': case tok_div: case tok_mod: + Ok = Numeric(LT) && Numeric(RT); + break; + case tok_and: case tok_or: + Ok = LT->Kind == TypeKind::Boolean && RT->Kind == TypeKind::Boolean; + break; + case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge: + Ok = LT->Kind == RT->Kind || (Numeric(LT) && Numeric(RT)); + break; + } + if (!Ok) { + reportError("Operator %s cannot be applied to %s and %s", + operatorSpelling(Op).c_str(), LT->Name.c_str(), RT->Name.c_str()); + return nullptr; + } return ArithHandler->emitBinaryOp(Op, L, R, Line); } diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc index ef079b7..8d54221 100644 --- a/lib/CodeGen/TypeCheckGen.cc +++ b/lib/CodeGen/TypeCheckGen.cc @@ -99,6 +99,21 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) { Src->Name.c_str(), Target->Name.c_str()); return nullptr; } + // Builtin scalars never convert into one another implicitly, except + // INTEGER -> REAL: BOOLEAN and CHAR are not numbers (use ASC / CHR). + auto IsPlain = [](const TypeInfo *T) { + return T->Kind == TypeKind::Integer || T->Kind == TypeKind::Real || + T->Kind == TypeKind::Boolean || T->Kind == TypeKind::Char; + }; + if (Src && IsPlain(Target) && IsPlain(Src)) { + bool Allowed = Src->Kind == Target->Kind || + (Src->Kind == TypeKind::Integer && Target->Kind == TypeKind::Real); + if (!Allowed) { + 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); } -- cgit v1.3.1-4-g156e