aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS/CONVENTIONS/TYPES.md6
-rw-r--r--doc/language/cpsc-language-overview.7.md2
-rw-r--r--doc/language/keyword/cpsc-keyword-and.7.md2
-rw-r--r--doc/language/keyword/cpsc-keyword-not.7.md2
-rw-r--r--doc/language/keyword/cpsc-keyword-or.7.md2
-rw-r--r--lib/CodeGen/ExpressionGen.cc27
-rw-r--r--lib/CodeGen/TypeCheckGen.cc15
7 files changed, 52 insertions, 4 deletions
diff --git a/AGENTS/CONVENTIONS/TYPES.md b/AGENTS/CONVENTIONS/TYPES.md
index 664782e..1c0d548 100644
--- a/AGENTS/CONVENTIONS/TYPES.md
+++ b/AGENTS/CONVENTIONS/TYPES.md
@@ -25,6 +25,12 @@ Allocation sizes still use LLVM sizeof; native C type widths use build-compiler
sizeof. C alias declarations precede casts using their names; the parser tracks
declared type names to distinguish `(Type)operand` from `(expression)`.
+**Builtin scalars are strictly typed.** `CodeGen::emitBinaryOp` (ExpressionGen.cc)
+rejects arithmetic on BOOLEAN/CHAR, AND/OR on non-BOOLEAN, and comparisons between
+different kinds (INTEGER with REAL is the only mixed pair); `emitCoercedExpr` allows
+only INTEGER->REAL between the four builtin scalars. BOOLEAN (i1) and CHAR (i8)
+compare unsigned. C scalars keep C semantics.
+
**User-kind typing is nominal, and checked upstream only.** Enum/Record/Pointer
identity is case-insensitive name equality (`P1 = ^INTEGER` and `P2 = ^INTEGER` are
incompatible; same-layout records too). Their LLVM representations are
diff --git a/doc/language/cpsc-language-overview.7.md b/doc/language/cpsc-language-overview.7.md
index 2106b90..05001bd 100644
--- a/doc/language/cpsc-language-overview.7.md
+++ b/doc/language/cpsc-language-overview.7.md
@@ -10,7 +10,7 @@ Keywords are uppercase and case-sensitive. Identifiers, including built-in funct
Assign with ASCII `<-`: `Count <- Count + 1`. Compare with `=`, `<>`, `<`, `>`, `<=`, and `>=`. Standard-library STRING comparisons compare contents, case-sensitively. Use `+`, `-`, `*`, and `/` for arithmetic; built-in `/` always returns REAL. Use `&` to join strings.
-User-defined types can overload operators with OVERLOAD FUNCTION OPERATOR; expressions then use the declared result type. See [cpsc-keyword-operator(7)](keyword/cpsc-keyword-operator.7.md). STRING cannot be implicitly converted to BOOLEAN.
+User-defined types can overload operators with OVERLOAD FUNCTION OPERATOR; expressions then use the declared result type. See [cpsc-keyword-operator(7)](keyword/cpsc-keyword-operator.7.md). STRING cannot be implicitly converted to BOOLEAN. Typing is strict: arithmetic and unary minus accept only INTEGER and REAL, a comparison needs operands of the same type (or INTEGER with REAL), and BOOLEAN, CHAR and INTEGER never convert into one another implicitly (only INTEGER converts to REAL). Use ASC and CHR to move between CHAR and INTEGER.
Precedence, from highest to lowest: unary operators, `* / DIV MOD`, `+ - &`, comparisons, AND, OR. Parentheses override precedence.
diff --git a/doc/language/keyword/cpsc-keyword-and.7.md b/doc/language/keyword/cpsc-keyword-and.7.md
index 1577798..7da90b7 100644
--- a/doc/language/keyword/cpsc-keyword-and.7.md
+++ b/doc/language/keyword/cpsc-keyword-and.7.md
@@ -12,7 +12,7 @@ left AND right
# DESCRIPTION
-Return BOOLEAN: true when both operands are true. Both operands are evaluated; AND does not short-circuit. AND binds more tightly than OR and less tightly than comparisons. Use parentheses for clarity.
+Return BOOLEAN: true when both operands are true. Both operands must be BOOLEAN; numbers and characters are rejected. Both operands are evaluated; AND does not short-circuit. AND binds more tightly than OR and less tightly than comparisons. Use parentheses for clarity.
# EXAMPLE
diff --git a/doc/language/keyword/cpsc-keyword-not.7.md b/doc/language/keyword/cpsc-keyword-not.7.md
index 36a051d..ba55fad 100644
--- a/doc/language/keyword/cpsc-keyword-not.7.md
+++ b/doc/language/keyword/cpsc-keyword-not.7.md
@@ -12,7 +12,7 @@ NOT expression
# DESCRIPTION
-Return BOOLEAN with the opposite truth value. NOT is a unary operator; parenthesize comparisons, for example `NOT (Count = 0)`.
+Return BOOLEAN with the opposite truth value. The operand must be BOOLEAN. NOT is a unary operator; parenthesize comparisons, for example `NOT (Count = 0)`.
# EXAMPLE
diff --git a/doc/language/keyword/cpsc-keyword-or.7.md b/doc/language/keyword/cpsc-keyword-or.7.md
index df861de..678ff19 100644
--- a/doc/language/keyword/cpsc-keyword-or.7.md
+++ b/doc/language/keyword/cpsc-keyword-or.7.md
@@ -12,7 +12,7 @@ left OR right
# DESCRIPTION
-Return BOOLEAN: true when either operand is true. Both operands are evaluated; OR does not short-circuit. OR binds less tightly than AND and comparisons.
+Return BOOLEAN: true when either operand is true. Both operands must be BOOLEAN; numbers and characters are rejected. Both operands are evaluated; OR does not short-circuit. OR binds less tightly than AND and comparisons.
# EXAMPLE
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);
}