aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS/PITFALLS/overload-template-operator.md14
-rw-r--r--AGENTS/SUBSYSTEMS/FUNCTIONS.md21
-rw-r--r--doc/language/keyword/cpsc-keyword-overload.7.md12
-rw-r--r--include/cps/CodeGen.h21
-rw-r--r--include/cps/FunctionGen.h1
-rw-r--r--lib/CodeGen/CTypeGen.cc1
-rw-r--r--lib/CodeGen/CallGen.cc107
-rw-r--r--lib/CodeGen/ConstructorGen.cc2
-rw-r--r--lib/CodeGen/Designator.cc14
-rw-r--r--lib/CodeGen/ExpressionGen.cc10
-rw-r--r--lib/CodeGen/FunctionGen.cc13
-rw-r--r--lib/CodeGen/TypeCheckGen.cc1
-rw-r--r--lib/CodeGen/ValueTypeGen.cc20
-rw-r--r--tests/iii/operator-global-errors/case.json4
-rw-r--r--tests/iii/overload-errors/case.json4
15 files changed, 165 insertions, 80 deletions
diff --git a/AGENTS/PITFALLS/overload-template-operator.md b/AGENTS/PITFALLS/overload-template-operator.md
index b7c139e..6f23238 100644
--- a/AGENTS/PITFALLS/overload-template-operator.md
+++ b/AGENTS/PITFALLS/overload-template-operator.md
@@ -6,12 +6,16 @@ Part of the [pitfalls index](../PITFALLS.md).
- OVERLOAD is reserved and prefixes FUNCTION/PROCEDURE (after PUBLIC/PRIVATE for
methods). Every same-name definition must opt in.
-- Only count, canonical parameter types and array rank distinguish signatures: return
- type, BYREF/BYVAL and array bounds do not.
+- Count, canonical parameter types, array rank and (for non-VOID definitions) return
+ type distinguish signatures; a PROCEDURE counts as VOID. BYREF/BYVAL and array
+ bounds do not.
- Calls match exact static types, even for a single OVERLOAD definition; ordinary
- functions still coerce arguments.
-- Call visibility filters the set, but duplicate signatures conflict across all
- inputs. MAIN and builtins cannot be overloaded.
+ functions still coerce arguments. A known expected result type (assignment, BYVAL
+ argument, RETURN, condition, subscript, CASE comparison) must match the overload's
+ return type exactly; `(T) x` only prefers T and otherwise converts. Without a
+ context, return-only-distinct candidates are ambiguous.
+- Call visibility filters the set, but duplicate signatures (parameters and return
+ type) conflict across all inputs. MAIN and builtins cannot be overloaded.
- NEW supports overloads; DECLARE, fields and arrays needing default construction
require a matching no-argument NEW.
- Private access is checked after selection, never used to select a public fallback.
diff --git a/AGENTS/SUBSYSTEMS/FUNCTIONS.md b/AGENTS/SUBSYSTEMS/FUNCTIONS.md
index 0297c48..4983853 100644
--- a/AGENTS/SUBSYSTEMS/FUNCTIONS.md
+++ b/AGENTS/SUBSYSTEMS/FUNCTIONS.md
@@ -26,14 +26,21 @@ canonical parameter list (MAIN uses internal __cps_main).
- `emitPrototype` records each function's pseudocode signature in a `FuncSig`
overload-set map (`FunctionGen::getOverloads`, filtered by unit visibility).
-- `resolveCall` selects exact static parameter types/ranks for OVERLOAD, while a single
- ordinary definition keeps implicit conversions.
-- Parameter keys exclude return types, modes and bounds (they never distinguish
- overloads); equivalent C aliases use
- CIdentity. Every same-name definition must be OVERLOAD; duplicate keys fail even
- across invisible units.
+- `resolveCall` selects exact static parameter types/ranks for OVERLOAD. When the call
+ site has an expected result type, the candidate must also return exactly that type;
+ a cast only prefers it. Without a context, return-only-distinct candidates are
+ ambiguous. A single ordinary definition keeps implicit conversions.
+- Parameter keys exclude return types, modes and bounds; the separate ReturnKey holds
+ the declared result (PROCEDURE is VOID). Equivalent C aliases use CIdentity.
+ Duplicate signatures require both keys to match, even across invisible units.
+- CodeGen records result contexts per expression node before the first type query
+ (`expectResult`/`resultContext`); assignment, BYVAL/RETURN, coercion and cast sites
+ register them, and call/operator selection consults the same table for typing and
+ emission.
- FunctionGen tracks prototype registration separately from source names and indexes
- signatures by LLVM link name for RETURN (`getSignatureByLink`).
+ signatures by LLVM link name for RETURN (`getSignatureByLink`). A non-VOID
+ OVERLOAD link name appends `:ReturnKey` after `Name(ParamKey)`; PROCEDURE, NEW and
+ OPERATOR<- keep their old names.
- `CodeGen::marshalCallArgs` consumes the selected FuncSig, and `getExprTypeInfo`
resolves the same calls without emitting IR.
- Prototype pre-registration (calls may precede definitions): see
diff --git a/doc/language/keyword/cpsc-keyword-overload.7.md b/doc/language/keyword/cpsc-keyword-overload.7.md
index 4012172..6a50e5a 100644
--- a/doc/language/keyword/cpsc-keyword-overload.7.md
+++ b/doc/language/keyword/cpsc-keyword-overload.7.md
@@ -24,13 +24,17 @@ ENDCLASS
OVERLOAD is a compiler extension, absent from the CIE guide. Put it immediately before FUNCTION or PROCEDURE, after PUBLIC or PRIVATE when an access keyword is present. OVERLOAD is reserved. Every definition sharing a case-insensitive name must specify OVERLOAD. Global functions, procedures, class methods and NEW constructors support it; MAIN and built-in functions do not.
-Signatures differ by parameter count, parameter types, or array rank. Return types, FUNCTION versus PROCEDURE, BYREF versus BYVAL, and declared array bounds do not distinguish signatures. Overloads may return different types when their parameter signatures differ. Nominal user types remain distinct; equivalent native C type aliases identify the same type.
+Signatures differ by parameter count, parameter types, array rank, or return type. BYREF versus BYVAL and declared array bounds do not distinguish signatures. A PROCEDURE counts as returning VOID, so it can coexist with a FUNCTION having the same parameter list; a call that cannot determine a result type is then ambiguous. Nominal user types remain distinct; equivalent native C type aliases identify the same type.
-Calls require an exact match to the arguments' static types. No implicit conversion is used to select an overload, even when only one OVERLOAD definition exists. Array arguments must be whole array variables with matching element types and rank. After selection, ordinary parameter-mode, array-bound and access checks apply. A failure reports `No matching overload for Name(T1, T2)`. Functions without OVERLOAD retain their ordinary argument conversions.
+Calls require an exact match to the arguments' static types. No implicit conversion is used to select an overload, even when only one OVERLOAD definition exists. Array arguments must be whole array variables with matching element types and rank. After selection, ordinary parameter-mode, array-bound and access checks apply. Functions without OVERLOAD retain their ordinary argument conversions and their implicit result conversions.
-Overloads may be defined in separate input files. A caller sees only its own and included units' definitions. Duplicate parameter signatures fail across all inputs, even when neither unit includes the other. See [cpsc-keyword-include(7)](cpsc-keyword-include.7.md).
+When the context requires a known result type, the selected overload must return exactly that type. This applies to assignment targets, BYVAL arguments, RETURN values, conditions, FOR bounds, subscripts and CASE comparisons. If the arguments match but no overload returns the required type, the error is `No matching overload for Name(T1, T2) returning T`. A cast `(T) expr` is weaker: it first prefers an overload returning T, then uses ordinary parameter selection and converts the result to T.
-Methods use the same rules; fields and methods cannot share a name. Constructor calls select NEW by their explicit arguments. DECLARE without arguments, class-valued fields and array elements select a parameterless NEW; if NEW exists but has no matching parameterless overload, compilation fails. PRIVATE checks apply to the selected method or constructor.
+Without an expected result type (for example OUTPUT, an operand of another operator or comparison, a CASE selector, a CALL statement, or an argument passed to another OVERLOAD call), a unique parameter match is selected. If several candidates share the same parameter list and differ only by return type, the call is ambiguous.
+
+Overloads may be defined in separate input files. A caller sees only its own and included units' definitions. Duplicate signatures fail across all inputs, even when neither unit includes the other; two definitions are duplicates only when both their parameters and their return types match. See [cpsc-keyword-include(7)](cpsc-keyword-include.7.md).
+
+Methods use the same rules, including result-type selection; fields and methods cannot share a name. Constructor calls select NEW by their explicit arguments. DECLARE without arguments, class-valued fields and array elements select a parameterless NEW; if NEW exists but has no matching parameterless overload, compilation fails. PRIVATE checks apply to the selected method or constructor.
Operator definitions use OVERLOAD FUNCTION OPERATOR followed by the operator symbol or keyword. They support global and member forms with exact operand types; see [cpsc-keyword-operator(7)](cpsc-keyword-operator.7.md).
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h
index ffd589c..99630ec 100644
--- a/include/cps/CodeGen.h
+++ b/include/cps/CodeGen.h
@@ -79,12 +79,21 @@ class CodeGen {
std::string CurrentClass;
const std::vector<FunctionDefAST *> *findMethod(const std::string &Class, const std::string &Name) const;
bool isRegisteredMethod(const std::string &Class, const PrototypeAST *Method) const;
+ // The type a call or operator result must have where it is used. A cast
+ // only prefers its type (Exact = false) and otherwise converts the result.
+ struct ResultContext { const TypeInfo *Type = nullptr; bool Exact = true; };
+ std::map<const ExprAST *, ResultContext> ResultContexts;
+ void expectResult(const ExprAST *E, const TypeInfo *Type, bool Exact = true);
+ const ResultContext *resultContext(const ExprAST *E) const;
const FuncSig *resolveMethod(const std::string &Class, const std::string &Name,
- const std::vector<std::unique_ptr<ExprAST>> &Args);
- llvm::Value *emitMemberCall(MemberCallExprAST *Call);
+ const std::vector<std::unique_ptr<ExprAST>> &Args,
+ const ResultContext *Context = nullptr);
+ llvm::Value *emitMemberCall(MemberCallExprAST *Call,
+ const ResultContext *Context = nullptr);
llvm::Value *emitMethodCall(const std::string &Class, llvm::Value *Receiver,
const std::string &Name,
- const std::vector<std::unique_ptr<ExprAST>> &Args, int Line);
+ const std::vector<std::unique_ptr<ExprAST>> &Args, int Line,
+ const ResultContext *Context = nullptr);
bool typeNeedsDestruction(const std::string &TypeName) const;
bool emitDestroy(const std::string &TypeName, llvm::Value *Addr, int Line);
bool typeNeedsConstruction(const std::string &TypeName) const;
@@ -204,10 +213,12 @@ class CodeGen {
llvm::Value *emitIntBuiltin(ExprAST *Arg, int Line);
llvm::Value *emitCall(const std::string &Callee,
const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line,
- const std::vector<std::string> *Explicit = nullptr);
+ const std::vector<std::string> *Explicit = nullptr,
+ const ResultContext *Context = nullptr);
const FuncSig *resolveCall(const std::string &Name,
const std::vector<std::unique_ptr<ExprAST>> &Args,
- const std::vector<std::string> *Explicit = nullptr);
+ const std::vector<std::string> *Explicit = nullptr,
+ const ResultContext *Context = nullptr);
struct OperatorResolution {
const FuncSig *Signature = nullptr;
bool Ambiguous = false;
diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h
index f5e5ba2..c3c530b 100644
--- a/include/cps/FunctionGen.h
+++ b/include/cps/FunctionGen.h
@@ -40,6 +40,7 @@ struct FuncSig {
std::string Name;
std::string LinkName;
std::string ParamKey;
+ std::string ReturnKey;
bool IsOverload = false;
const PrototypeAST *Prototype = nullptr;
std::string ReturnTypeName;
diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc
index 90f0aa3..b12537a 100644
--- a/lib/CodeGen/CTypeGen.cc
+++ b/lib/CodeGen/CTypeGen.cc
@@ -49,6 +49,7 @@ Value *CodeGen::emitCast(CastExprAST *Cast) {
reportError("Invalid cast target %s", Cast->getTypeName().c_str());
return nullptr;
}
+ expectResult(Operand, Target, /*Exact=*/false);
if (isWholeArrayVar(Operand)) {
reportError("Cannot cast a whole array; index it");
return nullptr;
diff --git a/lib/CodeGen/CallGen.cc b/lib/CodeGen/CallGen.cc
index 43c0dd4..30f0d61 100644
--- a/lib/CodeGen/CallGen.cc
+++ b/lib/CodeGen/CallGen.cc
@@ -171,7 +171,8 @@ Value *CodeGen::emitVariadicArgument(ExprAST *Expr, const std::string &Callee, s
// Resolve using static source types only; argument evaluation belongs to marshalling.
const FuncSig *CodeGen::resolveCall(const std::string &Name,
const std::vector<std::unique_ptr<ExprAST>> &Args,
- const std::vector<std::string> *Explicit) {
+ const std::vector<std::string> *Explicit,
+ const ResultContext *Context) {
auto Overloads = FuncGen->getOverloads(Name);
bool HasTemplates = hasFunctionTemplate(Name);
bool HasExplicit = Explicit && !Explicit->empty();
@@ -202,52 +203,77 @@ const FuncSig *CodeGen::resolveCall(const std::string &Name,
if (Rank) Key += "ARRAY" + std::to_string(Rank) + " OF ";
Key += Type ? Type->Name : "?";
}
- const FuncSig *Ordinary = nullptr;
- for (const FuncSig *Sig : Overloads) {
- if (HasExplicit) break;
- if (Sig->Params.size() != Actual.size()) continue;
- bool Match = true;
- for (size_t I = 0; I < Actual.size(); ++I) {
- const ParamSig &P = Sig->Params[I];
- if (Actual[I].Rank != (P.IsArray ? P.Rank : 0) || !Actual[I].Type ||
- !Actual[I].Type->sameType(Types->resolve(P.TypeName))) {
- Match = false;
- break;
- }
- }
- if (Match) {
- if (Ordinary && HasTemplates) {
- reportError("Ambiguous call to %s(%s)", Name.c_str(), Key.c_str());
- return nullptr;
- }
- Ordinary = Sig;
- if (!HasTemplates) return Sig;
- }
- }
- if (Ordinary) return Ordinary;
- // Failing an exact match, CHAR and STRING arguments convert into one
- // another; the overload must be unique.
- if (!HasTemplates && !HasExplicit) {
- auto Scalar = [](const TypeInfo *T) { return T && (T->Kind == TypeKind::Char || T->isString()); };
- const FuncSig *Converted = nullptr;
+ // A context with a type prefers candidates that return it; a strict
+ // context requires one. Without a context (or after a cast's preference
+ // finds nothing) selection stays parameter-only.
+ const bool PreferResult = Context && Context->Type;
+ auto ReturnsWanted = [&](const FuncSig *Sig) {
+ const TypeInfo *Ret = resolveType(Sig->ReturnTypeName);
+ return Ret && Ret->sameType(Context->Type);
+ };
+ auto Scalar = [](const TypeInfo *T) { return T && (T->Kind == TypeKind::Char || T->isString()); };
+ bool ParamMatched = false;
+ int Count = 0;
+ // One layer of parameter matching. Lenient adds CHAR/STRING conversions;
+ // FilterReturn keeps only candidates whose result is the wanted type.
+ auto Scan = [&](bool Lenient, bool FilterReturn) -> const FuncSig * {
+ const FuncSig *First = nullptr;
+ Count = 0;
for (const FuncSig *Sig : Overloads) {
+ if (HasExplicit) break;
if (Sig->Params.size() != Actual.size()) continue;
bool Match = true;
for (size_t I = 0; I < Actual.size() && Match; ++I) {
const ParamSig &P = Sig->Params[I];
const TypeInfo *Formal = Types->resolve(P.TypeName);
- Match = Actual[I].Rank == (P.IsArray ? P.Rank : 0) && Actual[I].Type &&
- (Actual[I].Type->sameType(Formal) ||
- (!P.IsByRef && !P.IsArray && Scalar(Actual[I].Type) && Scalar(Formal)));
+ bool SameRank = Actual[I].Rank == (P.IsArray ? P.Rank : 0);
+ bool Same = SameRank && Actual[I].Type && Actual[I].Type->sameType(Formal);
+ Match = Same ||
+ (Lenient && SameRank && Actual[I].Type && !P.IsByRef && !P.IsArray &&
+ Scalar(Actual[I].Type) && Scalar(Formal));
}
if (!Match) continue;
- if (Converted) {
- reportError("Ambiguous call to %s(%s)", Name.c_str(), Key.c_str());
- return nullptr;
- }
- Converted = Sig;
+ ParamMatched = true;
+ if (FilterReturn && !ReturnsWanted(Sig)) continue;
+ ++Count;
+ if (!First) First = Sig;
}
- if (Converted) return Converted;
+ return First;
+ };
+ auto ReportAmbiguous = [&](bool FilterReturn) {
+ if (FilterReturn)
+ reportError("Ambiguous call to %s(%s)", Name.c_str(), Key.c_str());
+ else
+ reportError("Ambiguous call to %s(%s); the return type cannot be determined from context",
+ Name.c_str(), Key.c_str());
+ };
+ // Exact parameters first, then CHAR/STRING conversions.
+ bool Ambiguous = false;
+ auto Try = [&](bool FilterReturn, const FuncSig *&Selected) {
+ Selected = Scan(false, FilterReturn);
+ if (Count > 1) {
+ ReportAmbiguous(FilterReturn);
+ Ambiguous = true;
+ return;
+ }
+ if (Selected || HasTemplates || HasExplicit) return;
+ Selected = Scan(true, FilterReturn);
+ if (Count > 1) {
+ ReportAmbiguous(FilterReturn);
+ Ambiguous = true;
+ }
+ };
+
+ const FuncSig *Selected = nullptr;
+ if (PreferResult) {
+ Try(true, Selected);
+ if (Ambiguous) return nullptr;
+ if (Selected) return Selected;
+ }
+ if (!Context || !Context->Exact) {
+ Try(false, Selected);
+ if (Ambiguous) return nullptr;
+ if (Selected) return Selected;
}
if (HasTemplates) {
// A matching non-variadic template wins before any variadic candidate.
@@ -323,6 +349,11 @@ const FuncSig *CodeGen::resolveCall(const std::string &Name,
if (Selected) return instantiateFunction(*Selected, SelectedBindings);
}
}
+ if (Context && Context->Exact && ParamMatched) {
+ reportError("No matching overload for %s(%s) returning %s", Name.c_str(), Key.c_str(),
+ Context->Type->Name.c_str());
+ return nullptr;
+ }
reportError("No matching overload for %s(%s)", Name.c_str(), Key.c_str());
return nullptr;
}
diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc
index e956f9d..f598494 100644
--- a/lib/CodeGen/ConstructorGen.cc
+++ b/lib/CodeGen/ConstructorGen.cc
@@ -20,6 +20,7 @@ void CodeGen::transferObjects(const std::vector<size_t> &Objects) {
Value *CodeGen::emitOwnedValue(ExprAST *Expr, const TypeInfo *Type,
std::vector<size_t> &Transfers) {
+ expectResult(Expr, Type);
if (!Type || (!Type->IsClass && !containsString(Type))) return emitCoercedExpr(Expr, Type);
const TypeInfo *Source = getExprTypeInfo(Expr);
// A CHAR argument becomes a fresh STRING temporary, owned like any other.
@@ -146,6 +147,7 @@ Value *CodeGen::symbolAddress(const SymbolInfo &Info) {
void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const SymbolInfo *Symbol) {
const TypeInfo *Info = resolveType(Target.TypeName);
+ expectResult(Expr, Info);
if (auto *New = dynamic_cast<NewExprAST*>(Expr)) {
SaveAndRestore<int> Location(DiagnosticLine, New->getLine());
const TypeInfo *Class = resolveType(New->getClassName());
diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc
index 6737937..d790d27 100644
--- a/lib/CodeGen/Designator.cc
+++ b/lib/CodeGen/Designator.cc
@@ -96,16 +96,17 @@ bool CodeGen::isRegisteredMethod(const std::string &Class, const PrototypeAST *M
}
const FuncSig *CodeGen::resolveMethod(const std::string &Class, const std::string &Name,
- const std::vector<std::unique_ptr<ExprAST>> &Args) {
+ const std::vector<std::unique_ptr<ExprAST>> &Args,
+ const ResultContext *Context) {
const auto *Methods = findMethod(Class, Name);
if (!Methods) {
reportError("Type %s has no method %s", Class.c_str(), Name.c_str());
return nullptr;
}
- return resolveCall(Methods->front()->getProto()->getLinkName(), Args);
+ return resolveCall(Methods->front()->getProto()->getLinkName(), Args, nullptr, Context);
}
-Value *CodeGen::emitMemberCall(MemberCallExprAST *Call) {
+Value *CodeGen::emitMemberCall(MemberCallExprAST *Call, const ResultContext *Context) {
SaveAndRestore<int> Location(DiagnosticLine, Call->getLine());
LValueInfo Receiver;
if (isObjectLValue(Call->getReceiver())) {
@@ -120,14 +121,15 @@ Value *CodeGen::emitMemberCall(MemberCallExprAST *Call) {
if (!Receiver.Addr) return nullptr;
}
return emitMethodCall(Receiver.TypeName, Receiver.Addr, Call->getMethod(),
- Call->getArgs(), Call->getLine());
+ Call->getArgs(), Call->getLine(), Context);
}
Value *CodeGen::emitMethodCall(const std::string &Class, Value *Receiver,
const std::string &Name,
- const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line) {
+ const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line,
+ const ResultContext *Context) {
SaveAndRestore<int> Location(DiagnosticLine, Line);
- const FuncSig *Sig = resolveMethod(Class, Name, ArgExprs);
+ const FuncSig *Sig = resolveMethod(Class, Name, ArgExprs, Context);
if (!Sig) return nullptr;
const PrototypeAST *Method = Sig->Prototype;
if (!Method->isPublic() && !sameIdentifier(CurrentClass, Class)) {
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index 45583d3..38155e4 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -154,10 +154,11 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
}
if (auto *Call = dynamic_cast<MemberCallExprAST*>(Expr))
- return emitMemberCall(Call);
+ return emitMemberCall(Call, resultContext(Expr));
if (auto *Call = dynamic_cast<CallExprAST*>(Expr))
- return emitCall(Call->getCallee(), Call->getArgs(), Call->getLine(), &Call->getTemplateArgs());
+ return emitCall(Call->getCallee(), Call->getArgs(), Call->getLine(),
+ &Call->getTemplateArgs(), resultContext(Expr));
if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) {
LValueInfo LV;
@@ -263,7 +264,8 @@ Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT,
Value *CodeGen::emitCall(const std::string &Callee,
const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line,
- const std::vector<std::string> *Explicit) {
+ const std::vector<std::string> *Explicit,
+ const ResultContext *Context) {
SaveAndRestore<int> Location(DiagnosticLine, Line ? Line : DiagnosticLine);
std::string Name = Callee;
if (const BuiltinInfo *B = findBuiltin(Name)) {
@@ -303,7 +305,7 @@ Value *CodeGen::emitCall(const std::string &Callee,
std::vector<Value*> Args;
std::vector<const TypeInfo *> Variadic;
- const FuncSig *Sig = resolveCall(Name, ArgExprs, Explicit);
+ const FuncSig *Sig = resolveCall(Name, ArgExprs, Explicit, Context);
if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args, &Variadic)) return nullptr;
return FuncGen->emitCall(*Sig, Args, Variadic);
}
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index c233205..2e927f9 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -229,6 +229,10 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
}
Type *RetType = getLLVMType(Proto->getReturnType());
+ const TypeInfo *ReturnInfo = Types.resolve(Proto->getReturnType());
+ Sig.ReturnKey = ReturnInfo
+ ? (ReturnInfo->isC() ? "C:" + ReturnInfo->CIdentity : ReturnInfo->Name)
+ : Proto->getReturnType();
FunctionType *FT = FunctionType::get(RetType, ArgTypes, false);
auto &Overloads = Signatures[Sig.Name];
for (const FuncSig &Existing : Overloads) {
@@ -241,7 +245,8 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
Proto->getName().c_str(), Proto->getName().c_str());
return nullptr;
}
- if (sameIdentifier(Existing.ParamKey, Sig.ParamKey)) {
+ if (sameIdentifier(Existing.ParamKey, Sig.ParamKey) &&
+ sameIdentifier(Existing.ReturnKey, Sig.ReturnKey)) {
if (Existing.OwnerUnit != CurrentUnit)
reportError("Duplicate definition %s in %s and %s", Sig.Name.c_str(),
UnitPaths[Existing.OwnerUnit].c_str(), UnitPaths[CurrentUnit].c_str());
@@ -250,6 +255,12 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
}
}
Sig.LinkName = Sig.Name + (Sig.IsOverload ? "(" + Sig.ParamKey + ")" : "");
+ // Only the return type distinguishes non-void overloads that share a
+ // parameter list, so it enters their link name. PROCEDURE, NEW and
+ // OPERATOR<- keep the established names.
+ if (Sig.IsOverload && ReturnInfo && !ReturnInfo->isVoid() &&
+ !sameIdentifier(Proto->getName(), "NEW") && Proto->getOperator() != tok_assign)
+ Sig.LinkName += ":" + Sig.ReturnKey;
Function *F = Function::Create(FT,
Proto->getOwnerClass().empty() && sameIdentifier(Proto->getName(), "MAIN")
? Function::InternalLinkage : Function::ExternalLinkage,
diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc
index 0f94859..6e9cac2 100644
--- a/lib/CodeGen/TypeCheckGen.cc
+++ b/lib/CodeGen/TypeCheckGen.cc
@@ -14,6 +14,7 @@ using namespace cps;
Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
SaveAndRestore<int> Location(DiagnosticLine, E && E->getLine() ? E->getLine() : DiagnosticLine);
if (!E || !Target) return nullptr;
+ expectResult(E, Target);
// A whole-array reference evaluates to its raw data pointer, and
// getExprTypeInfo reports the ELEMENT type for it — both would slip
diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc
index af32d74..dddb1b0 100644
--- a/lib/CodeGen/ValueTypeGen.cc
+++ b/lib/CodeGen/ValueTypeGen.cc
@@ -8,6 +8,19 @@ using namespace cps;
using namespace cps::detail;
+// The first context registered for a node wins: it is the outermost place the
+// result is consumed, and later queries must agree with that selection.
+void CodeGen::expectResult(const ExprAST *E, const TypeInfo *Type, bool Exact) {
+ if (!E || !Type) return;
+ ResultContexts.emplace(E, ResultContext{Type, Exact});
+}
+
+const CodeGen::ResultContext *CodeGen::resultContext(const ExprAST *E) const {
+ if (!E) return nullptr;
+ auto It = ResultContexts.find(E);
+ return It == ResultContexts.end() ? nullptr : &It->second;
+}
+
const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) {
if (!Expr) return nullptr;
@@ -128,7 +141,9 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) {
if (auto *Call = dynamic_cast<MemberCallExprAST*>(Expr)) {
const TypeInfo *Receiver = getExprTypeInfo(Call->getReceiver());
- const FuncSig *Sig = Receiver ? resolveMethod(Receiver->Name, Call->getMethod(), Call->getArgs()) : nullptr;
+ const FuncSig *Sig = Receiver
+ ? resolveMethod(Receiver->Name, Call->getMethod(), Call->getArgs(), resultContext(Expr))
+ : nullptr;
if (!Sig || (!Sig->Prototype->isPublic() && !sameIdentifier(CurrentClass, Receiver->Name))) return nullptr;
return resolveType(Sig->ReturnTypeName);
}
@@ -142,7 +157,8 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) {
}
return resolveType(B->ReturnTypeName);
}
- if (const FuncSig *Sig = resolveCall(Name, Call->getArgs(), &Call->getTemplateArgs())) {
+ if (const FuncSig *Sig = resolveCall(Name, Call->getArgs(), &Call->getTemplateArgs(),
+ resultContext(Expr))) {
return resolveType(Sig->ReturnTypeName);
}
diff --git a/tests/iii/operator-global-errors/case.json b/tests/iii/operator-global-errors/case.json
index a267007..b12dc98 100644
--- a/tests/iii/operator-global-errors/case.json
+++ b/tests/iii/operator-global-errors/case.json
@@ -15,10 +15,6 @@
{
"reason": "Invalid operand count",
"line": 17
- },
- {
- "reason": "cannot be redefined",
- "line": 20
}
]
}
diff --git a/tests/iii/overload-errors/case.json b/tests/iii/overload-errors/case.json
index f720889..6e2156e 100644
--- a/tests/iii/overload-errors/case.json
+++ b/tests/iii/overload-errors/case.json
@@ -5,10 +5,6 @@
"line": 4
},
{
- "reason": "Function Ret cannot be redefined",
- "line": 10
- },
- {
"reason": "Function Mode cannot be redefined",
"line": 15
},