aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'lib/CodeGen')
-rw-r--r--lib/CodeGen/CallGen.cc186
-rw-r--r--lib/CodeGen/ConstructorGen.cc13
-rw-r--r--lib/CodeGen/ExpressionGen.cc2
-rw-r--r--lib/CodeGen/FunctionGen.cc10
-rw-r--r--lib/CodeGen/OperatorGen.cc69
-rw-r--r--lib/CodeGen/TemplateClone.cc1
-rw-r--r--lib/CodeGen/TypeCheckGen.cc38
7 files changed, 210 insertions, 109 deletions
diff --git a/lib/CodeGen/CallGen.cc b/lib/CodeGen/CallGen.cc
index 0741763..f5f1eda 100644
--- a/lib/CodeGen/CallGen.cc
+++ b/lib/CodeGen/CallGen.cc
@@ -210,10 +210,9 @@ const FuncSig *CodeGen::resolveCall(const std::string &Name,
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;
+ // One layer of parameter matching. Lenient admits implicit constructors;
// FilterReturn keeps only candidates whose result is the wanted type.
auto Scan = [&](bool Lenient, bool FilterReturn) -> const FuncSig * {
const FuncSig *First = nullptr;
@@ -230,7 +229,7 @@ const FuncSig *CodeGen::resolveCall(const std::string &Name,
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));
+ canImplicitlyConvert(Actual[I].Type, Formal));
}
if (!Match) continue;
ParamMatched = true;
@@ -254,106 +253,113 @@ const FuncSig *CodeGen::resolveCall(const std::string &Name,
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;
+ bool AllowConversions = 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);
+ Selected = Scan(AllowConversions, 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.
- for (bool Variadic : {false, true}) {
- const TemplateDefinition *Selected = nullptr;
- detail::TemplateBindings SelectedBindings;
- for (const auto &Definition : Templates.at(Name)) {
- auto *Function = Definition.Decl->getFunction();
- if (!Function || Definition.Decl->isVariadic() != Variadic || !isVisible(Definition.OwnerUnit)) continue;
- const auto &Params = Function->getProto()->getParams();
- const auto &TemplateParams = Definition.Decl->getParams();
- bool HasPackParam = !Params.empty() && Params.back().IsPack;
- size_t FixedCount = Params.size() - HasPackParam;
- if (HasPackParam ? Actual.size() < FixedCount : Actual.size() != FixedCount) continue;
- detail::TemplateBindings Bindings;
- for (const auto &Param : TemplateParams) Bindings.emplace(Param.Name, std::vector<std::string>{});
- SaveAndRestore<bool> Speculation(SpeculativeTemplateResolution, true);
- bool Match = true;
- if (HasExplicit) {
- size_t FixedTypes = TemplateParams.size() - Variadic;
- if (!Variadic && Explicit->size() > FixedTypes) continue;
- for (size_t I = 0; I < Explicit->size(); ++I) {
- auto Type = canonicalTemplateArgument((*Explicit)[I]);
- if (!Type) { Match = false; break; }
- Bindings.at(TemplateParams[std::min(I, FixedTypes)].Name).push_back(*Type);
- }
- }
- if (!Match) continue;
- for (size_t I = 0; I < FixedCount; ++I) {
- const auto &P = Params[I];
- const TypeInfo *Type = Actual[I].Type;
- if (!Type || Type->isVoid() || Actual[I].Rank != (P.IsArray ? P.Rank : 0)) { Match = false; break; }
- auto Binding = Bindings.find(P.TypeName);
- if (Binding != Bindings.end()) {
- if (Binding->second.empty()) Binding->second.push_back(Type->Name);
- else if (!Type->sameType(resolveType(Binding->second.front()))) { Match = false; break; }
+ // Exhaust exact ordinary/template candidates before considering conversions.
+ for (bool ConversionLayer : {false, true}) {
+ AllowConversions = ConversionLayer;
+ 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.
+ for (bool Variadic : {false, true}) {
+ const TemplateDefinition *Selected = nullptr;
+ detail::TemplateBindings SelectedBindings;
+ for (const auto &Definition : Templates.at(Name)) {
+ auto *Function = Definition.Decl->getFunction();
+ if (!Function || Definition.Decl->isVariadic() != Variadic || !isVisible(Definition.OwnerUnit)) continue;
+ const auto &Params = Function->getProto()->getParams();
+ const auto &TemplateParams = Definition.Decl->getParams();
+ bool HasPackParam = !Params.empty() && Params.back().IsPack;
+ size_t FixedCount = Params.size() - HasPackParam;
+ if (HasPackParam ? Actual.size() < FixedCount : Actual.size() != FixedCount) continue;
+ detail::TemplateBindings Bindings;
+ for (const auto &Param : TemplateParams) Bindings.emplace(Param.Name, std::vector<std::string>{});
+ SaveAndRestore<bool> Speculation(SpeculativeTemplateResolution, true);
+ bool Match = true;
+ if (HasExplicit) {
+ size_t FixedTypes = TemplateParams.size() - Variadic;
+ if (!Variadic && Explicit->size() > FixedTypes) continue;
+ for (size_t I = 0; I < Explicit->size(); ++I) {
+ auto Type = canonicalTemplateArgument((*Explicit)[I]);
+ if (!Type) { Match = false; break; }
+ Bindings.at(TemplateParams[std::min(I, FixedTypes)].Name).push_back(*Type);
+ }
}
- }
- if (HasPackParam) {
- auto &Pack = Bindings.at(Params.back().TypeName);
- // Explicit pack types constrain a prefix; deduction fills the rest.
- if (Pack.size() > Actual.size() - FixedCount) Match = false;
- for (size_t I = FixedCount; Match && I < Actual.size(); ++I) {
+ if (!Match) continue;
+ for (size_t I = 0; I < FixedCount; ++I) {
+ const auto &P = Params[I];
const TypeInfo *Type = Actual[I].Type;
- size_t Index = I - FixedCount;
- if (!Type || Type->isVoid() || Actual[I].Rank) { Match = false; break; }
- if (Index < Pack.size()) {
- if (!Type->sameType(resolveType(Pack[Index]))) Match = false;
- } else Pack.push_back(Type->Name);
+ if (!Type || Type->isVoid() || Actual[I].Rank != (P.IsArray ? P.Rank : 0)) { Match = false; break; }
+ auto Binding = Bindings.find(P.TypeName);
+ if (Binding != Bindings.end()) {
+ if (Binding->second.empty()) Binding->second.push_back(Type->Name);
+ else if (!Type->sameType(resolveType(Binding->second.front())) &&
+ !(AllowConversions && P.Mode == PassMode::ByVal && !P.IsArray &&
+ canImplicitlyConvert(Type, resolveType(Binding->second.front())))) {
+ Match = false; break;
+ }
+ }
}
+ if (HasPackParam) {
+ auto &Pack = Bindings.at(Params.back().TypeName);
+ // Explicit pack types constrain a prefix; deduction fills the rest.
+ if (Pack.size() > Actual.size() - FixedCount) Match = false;
+ for (size_t I = FixedCount; Match && I < Actual.size(); ++I) {
+ const TypeInfo *Type = Actual[I].Type;
+ size_t Index = I - FixedCount;
+ if (!Type || Type->isVoid() || Actual[I].Rank) { Match = false; break; }
+ if (Index < Pack.size()) {
+ if (!Type->sameType(resolveType(Pack[Index])) &&
+ !(AllowConversions && Params.back().Mode == PassMode::ByVal &&
+ canImplicitlyConvert(Type, resolveType(Pack[Index])))) Match = false;
+ } else Pack.push_back(Type->Name);
+ }
+ }
+ for (const auto &Param : TemplateParams)
+ if (!Param.IsPack && Bindings.at(Param.Name).empty()) Match = false;
+ if (!Match) continue;
+ // Composite parameter types can use bindings deduced at later positions,
+ // but do not themselves contribute deductions.
+ detail::TemplateClone Substitute(Bindings, [&](int, const std::string &) { Match = false; });
+ for (size_t I = 0; I < FixedCount; ++I) {
+ const auto &TypeName = Params[I].TypeName;
+ auto Binding = Bindings.find(TypeName);
+ std::string Expected = Binding != Bindings.end() ? Binding->second.front()
+ : TypeName.find('<') != std::string::npos ? Substitute.type(TypeName) : TypeName;
+ if (!Match || (!Actual[I].Type->sameType(resolveType(Expected)) &&
+ !(AllowConversions && Params[I].Mode == PassMode::ByVal && !Params[I].IsArray &&
+ canImplicitlyConvert(Actual[I].Type, resolveType(Expected))))) {
+ Match = false; break;
+ }
+ }
+ if (!Match) continue;
+ if (Selected) {
+ reportError("Ambiguous call to %s(%s)", Name.c_str(), Key.c_str());
+ return nullptr;
+ }
+ Selected = &Definition;
+ SelectedBindings = std::move(Bindings);
}
- for (const auto &Param : TemplateParams)
- if (!Param.IsPack && Bindings.at(Param.Name).empty()) Match = false;
- if (!Match) continue;
- // Composite parameter types can use bindings deduced at later positions,
- // but do not themselves contribute deductions.
- detail::TemplateClone Substitute(Bindings, [&](int, const std::string &) { Match = false; });
- for (size_t I = 0; I < FixedCount; ++I) {
- const auto &TypeName = Params[I].TypeName;
- auto Binding = Bindings.find(TypeName);
- std::string Expected = Binding != Bindings.end() ? Binding->second.front()
- : TypeName.find('<') != std::string::npos ? Substitute.type(TypeName) : TypeName;
- if (!Match || !Actual[I].Type->sameType(resolveType(Expected))) { Match = false; break; }
- }
- if (!Match) continue;
- if (Selected) {
- reportError("Ambiguous call to %s(%s)", Name.c_str(), Key.c_str());
- return nullptr;
- }
- Selected = &Definition;
- SelectedBindings = std::move(Bindings);
+ if (Selected) return instantiateFunction(*Selected, SelectedBindings);
}
- if (Selected) return instantiateFunction(*Selected, SelectedBindings);
}
}
if (Context && Context->Exact && ParamMatched) {
diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc
index da89f59..81c448f 100644
--- a/lib/CodeGen/ConstructorGen.cc
+++ b/lib/CodeGen/ConstructorGen.cc
@@ -24,9 +24,9 @@ Value *CodeGen::emitOwnedValue(ExprAST *Expr, const TypeInfo *Type,
const TypeInfo *Source = getExprTypeInfo(Expr);
// A CHAR argument becomes a fresh STRING temporary, owned like any other.
bool FromChar = Source && Type->isString() && Source->Kind == TypeKind::Char;
- if (!Source || (!Source->sameType(Type) && !FromChar)) return emitCoercedExpr(Expr, Type);
+ if (!Source) return emitCoercedExpr(Expr, Type);
Value *Value = nullptr;
- if (!FromChar && isObjectLValue(Expr)) {
+ if (!FromChar && Source->sameType(Type) && isObjectLValue(Expr)) {
LValueInfo LV;
const auto *Var = dynamic_cast<VariableExprAST *>(Expr);
const auto *Symbol = Var ? getSymbolInfo(Var->getName()) : nullptr;
@@ -241,7 +241,14 @@ void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const Symb
return;
}
Value *Val = emitCoercedExpr(Expr, Info);
- if (Val) Builder->CreateStore(Val, Target.Addr);
+ if (Val && Info && Info->IsClass && ExprType && !ExprType->sameType(Info)) {
+ materializeOperatorReceiver(Val, Info);
+ std::vector<size_t> Transfers;
+ if (typeNeedsDestruction(Info->Name)) Transfers.push_back(StackObjects.size() - 1);
+ if (!emitDestroy(Info->Name, Target.Addr, DiagnosticLine)) return;
+ Builder->CreateStore(Val, Target.Addr);
+ transferObjects(Transfers);
+ } else if (Val) Builder->CreateStore(Val, Target.Addr);
}
bool CodeGen::typeNeedsDestruction(const std::string &TypeName) const {
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index 9f68a82..463dbed 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -249,7 +249,7 @@ Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT,
LT->Name.c_str(), RT->Name.c_str(), Context->Type->Name.c_str());
return nullptr;
}
- if (Overload.Signature) return emitOperatorValues(*Overload.Signature, L, LT, R);
+ if (Overload.Signature) return emitOperatorValues(*Overload.Signature, L, LT, R, RT);
if ((LT && LT->isC()) || (RT && RT->isC()))
return emitCBinaryOp(Op, L, LT, R, RT, Line);
if ((LT && LT->isUserKind()) || (RT && RT->isUserKind()))
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index a0c520d..7aa5d46 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -130,6 +130,15 @@ const FuncSig *FunctionGen::selectDefaultReturn(const std::vector<const FuncSig
// Copy-constructor, operator, MAIN and name-collision rules for a
// FUNCTION/PROCEDURE prototype; reports the first violation.
bool FunctionGen::validatePrototype(PrototypeAST *Proto) {
+ if (Proto->isImplicit()) {
+ const auto &P = Proto->getParams();
+ if (Proto->getOwnerClass().empty() || !sameIdentifier(Proto->getName(), "NEW") ||
+ !sameIdentifier(Proto->getReturnType(), "VOID") || P.size() != 1 ||
+ P[0].Mode != PassMode::ByVal || P[0].IsArray || P[0].IsPack) {
+ reportError("IMPLICIT requires a CLASS constructor NEW with one BYVAL non-array parameter");
+ return false;
+ }
+ }
if (Proto->isDefaultReturn()) {
const TypeInfo *Return = Types.resolve(Proto->getReturnType());
if (!Proto->isOverload() || (Return && Return->isVoid())) {
@@ -213,6 +222,7 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
Sig.OwnerUnit = CurrentUnit;
Sig.Name = Proto->getLinkName();
Sig.IsOverload = Proto->isOverload();
+ Sig.IsImplicit = Proto->isImplicit();
Sig.IsDefaultReturn = Proto->isDefaultReturn();
Sig.Prototype = Proto;
Sig.ReturnTypeName = Proto->getReturnType();
diff --git a/lib/CodeGen/OperatorGen.cc b/lib/CodeGen/OperatorGen.cc
index 7cfac00..15667ea 100644
--- a/lib/CodeGen/OperatorGen.cc
+++ b/lib/CodeGen/OperatorGen.cc
@@ -16,15 +16,39 @@ CodeGen::OperatorResolution CodeGen::resolveOperator(
auto Members = FuncGen->getOverloads(Left->Name + "." + operatorName(Op));
Candidates.insert(Candidates.end(), Members.begin(), Members.end());
}
- // An exact match wins; failing that, a CHAR operand may stand in for a
- // STRING parameter (it is converted when the arguments are marshalled).
- auto Accepts = [&](const TypeInfo *Actual, const std::string &Param, bool Lenient) {
- const TypeInfo *Formal = resolveType(Param);
+ auto Accepts = [&](const TypeInfo *Actual, const ParamSig &Param, bool Lenient) {
+ const TypeInfo *Formal = resolveType(Param.TypeName);
if (Actual->sameType(Formal)) return true;
- return Lenient && Actual->Kind == TypeKind::Char && Formal && Formal->isString();
+ // Keep builtin CHAR comparisons ahead of STRING construction.
+ if (Right && Left->Kind == TypeKind::Char && Right->Kind == TypeKind::Char &&
+ Op != '&' && Formal && Formal->isString()) return false;
+ return Lenient && !Param.IsByRef && !Param.IsArray && canImplicitlyConvert(Actual, Formal);
};
- // Two CHARs compare as CHARs; only '&' always builds a STRING.
- bool CanConvert = Right && (Op == '&' || Left->isString() || Right->isString());
+ // Builtin operations on exact operand types precede converted overloads.
+ // Raw C text is the exception: visible STRING operators supply text semantics.
+ auto Numeric = [](const TypeInfo *T) {
+ return T && (T->Kind == TypeKind::Integer || T->Kind == TypeKind::Real);
+ };
+ bool Comparison = Op == tok_eq || Op == tok_ne || Op == '<' || Op == '>' ||
+ Op == tok_le || Op == tok_ge;
+ bool Builtin = false;
+ if (!Right) {
+ Builtin = (Op == '-' && Numeric(Left)) ||
+ (Op == tok_not && Left->Kind == TypeKind::Boolean) ||
+ Left->Kind == TypeKind::CScalar;
+ } else {
+ Builtin = (Numeric(Left) && Numeric(Right) &&
+ (Comparison || Op == '+' || Op == '-' || Op == '*' || Op == '/' ||
+ Op == tok_div || Op == tok_mod)) ||
+ (Left->sameType(Right) &&
+ ((Comparison && (Left->Kind == TypeKind::Char ||
+ Left->Kind == TypeKind::Boolean || Left->isEnum())) ||
+ ((Op == tok_and || Op == tok_or) && Left->Kind == TypeKind::Boolean) ||
+ ((Op == tok_eq || Op == tok_ne) && Left->isPointer() &&
+ Left->CIdentity != "char*"))) ||
+ (Left->Kind == TypeKind::CScalar && Right->Kind == TypeKind::CScalar) ||
+ (Left->isEnum() && Right->Kind == TypeKind::Integer && (Op == '+' || Op == '-'));
+ }
const bool PreferResult = Context && Context->Type;
auto ReturnsWanted = [&](const FuncSig *Sig) {
const TypeInfo *Ret = resolveType(Sig->ReturnTypeName);
@@ -35,7 +59,7 @@ CodeGen::OperatorResolution CodeGen::resolveOperator(
Result.Signature = nullptr;
Result.Ambiguous = false;
for (bool Lenient : {false, true}) {
- if (Lenient && !CanConvert) break;
+ if (Lenient && Builtin) break;
const FuncSig *Selected = nullptr;
int Count = 0;
std::vector<const FuncSig *> Matches;
@@ -43,8 +67,8 @@ CodeGen::OperatorResolution CodeGen::resolveOperator(
bool Member = !Sig->Prototype->getOwnerClass().empty();
if (Sig->Prototype->getOperator() != Op ||
Sig->Params.size() != (Right ? 2u : 1u) - Member) continue;
- if (!Member && !Accepts(Left, Sig->Params[0].TypeName, Lenient)) continue;
- if (Right && !Accepts(Right, Sig->Params[Member ? 0 : 1].TypeName, Lenient)) continue;
+ if (!Member && !Accepts(Left, Sig->Params[0], Lenient)) continue;
+ if (Right && !Accepts(Right, Sig->Params[Member ? 0 : 1], Lenient)) continue;
ParamMatched = true;
if (FilterReturn && !ReturnsWanted(Sig)) continue;
++Count;
@@ -125,7 +149,7 @@ Value *CodeGen::emitOperatorCall(const FuncSig &Signature, ExprAST *Left, ExprAS
// CASE has already evaluated and captured its selector and label values.
Value *CodeGen::emitOperatorValues(const FuncSig &Signature, Value *Left,
- const TypeInfo *LeftType, Value *Right) {
+ const TypeInfo *LeftType, Value *Right, const TypeInfo *ActualRightType) {
if (!checkOperatorAccess(Signature)) return nullptr;
bool Member = !Signature.Prototype->getOwnerClass().empty();
std::vector<Value *> Args;
@@ -136,16 +160,31 @@ Value *CodeGen::emitOperatorValues(const FuncSig &Signature, Value *Left,
Builder->CreateStore(Val, Slot);
return Slot;
};
+ auto Prepare = [&](Value *Val, const TypeInfo *Actual, const TypeInfo *Formal) -> Value * {
+ if (!Val || !Actual || !Formal) return nullptr;
+ if (!Actual->sameType(Formal)) {
+ const FuncSig *Constructor = findImplicitConstructor(Actual, Formal);
+ if (!Constructor) return nullptr;
+ std::vector<size_t> SourceTransfers;
+ if (Actual->IsClass || containsString(Actual))
+ Val = copyClassValue(Actual, Capture(Val, Actual), SourceTransfers);
+ Val = emitImplicitConstruct(*Constructor, Formal, Val, SourceTransfers);
+ if (!Val) return nullptr;
+ materializeOperatorReceiver(Val, Formal);
+ if (typeNeedsDestruction(Formal->Name)) Transfers.push_back(StackObjects.size() - 1);
+ } else if (Formal->IsClass || containsString(Formal)) {
+ Val = copyClassValue(Formal, Capture(Val, Formal), Transfers);
+ }
+ return Val;
+ };
if (Member) Args.push_back(Capture(Left, LeftType));
else {
- if (LeftType->IsClass || containsString(LeftType))
- Left = copyClassValue(LeftType, Capture(Left, LeftType), Transfers);
+ Left = Prepare(Left, LeftType, resolveType(Signature.Params[0].TypeName));
if (!Left) return nullptr;
Args.push_back(Left);
}
const TypeInfo *RightType = resolveType(Signature.Params[Member ? 0 : 1].TypeName);
- if (RightType->IsClass || containsString(RightType))
- Right = copyClassValue(RightType, Capture(Right, RightType), Transfers);
+ Right = Prepare(Right, ActualRightType, RightType);
if (!Right) return nullptr;
Args.push_back(Right);
transferObjects(Transfers);
diff --git a/lib/CodeGen/TemplateClone.cc b/lib/CodeGen/TemplateClone.cc
index b0aaf0c..2d8b952 100644
--- a/lib/CodeGen/TemplateClone.cc
+++ b/lib/CodeGen/TemplateClone.cc
@@ -221,6 +221,7 @@ std::unique_ptr<FunctionDefAST> TemplateClone::function(const FunctionDefAST *So
Proto->isExternal(), OwnerClass, Proto->isPublic(), Proto->isOverload());
CopyProto->setOperator(Proto->getOperator());
CopyProto->setDefaultReturn(Proto->isDefaultReturn());
+ CopyProto->setImplicit(Proto->isImplicit());
CopyProto->setVariadic(Proto->isVariadic());
auto Copy = located(std::make_unique<FunctionDefAST>(std::move(CopyProto), body(Source->getBody())), Source);
PackNames = std::move(SavedNames);
diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc
index 6e9cac2..b730e93 100644
--- a/lib/CodeGen/TypeCheckGen.cc
+++ b/lib/CodeGen/TypeCheckGen.cc
@@ -7,6 +7,36 @@
using namespace llvm;
using namespace cps;
+// Candidate queries neither evaluate the source nor follow conversion chains.
+const FuncSig *CodeGen::findImplicitConstructor(const TypeInfo *Source, const TypeInfo *Target) {
+ if (!Source || !Target || !Target->IsClass || Source->sameType(Target)) return nullptr;
+ for (const FuncSig *Sig : FuncGen->getOverloads(Target->Name + ".NEW"))
+ if (Sig->IsImplicit && Sig->Params.size() == 1 &&
+ Source->sameType(resolveType(Sig->Params[0].TypeName))) return Sig;
+ return nullptr;
+}
+
+bool CodeGen::canImplicitlyConvert(const TypeInfo *Source, const TypeInfo *Target) {
+ if (!Source || !Target) return false;
+ if (Source->sameType(Target)) return true;
+ if ((Source->Kind == TypeKind::Char && Target->isString()) ||
+ (Source->isString() && Target->Kind == TypeKind::Char)) return true;
+ return findImplicitConstructor(Source, Target) != nullptr;
+}
+
+Value *CodeGen::emitImplicitConstruct(const FuncSig &Constructor, const TypeInfo *Target,
+ Value *Argument, const std::vector<size_t> &Transfers) {
+ if (!Argument || !checkOperatorAccess(Constructor)) return nullptr;
+ auto *Storage = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(),
+ Target->LLVMType, "implicit_object");
+ Builder->CreateStore(Types->getZeroValue(Target->Name), Storage);
+ if (!emitFieldDefaults(Target, Storage, DiagnosticLine)) return nullptr;
+ transferObjects(Transfers);
+ if (!FuncGen->emitCall(Constructor, {Storage, Argument})) return nullptr;
+ // Like a function result, the value is owned by its consumer.
+ return Builder->CreateLoad(Target->LLVMType, Storage, "implicit_value");
+}
+
// 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
@@ -38,6 +68,14 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
if (Src && Src->isString() && Target->Kind == TypeKind::Char)
return emitCharFromString(E, E->getLine() ? E->getLine() : DiagnosticLine);
+ if (const FuncSig *Constructor = findImplicitConstructor(Src, Target)) {
+ if (!checkOperatorAccess(*Constructor)) return nullptr;
+ std::vector<size_t> Transfers;
+ Value *Argument = emitOwnedValue(E, Src, Transfers);
+ if (!Argument) return nullptr;
+ return emitImplicitConstruct(*Constructor, Target, Argument, Transfers);
+ }
+
if (Target->isC() || (Src && Src->isC())) {
if (auto *Addr = dynamic_cast<AddrOfExprAST*>(E)) {
const PointerPayload *PP = Target->asPointer();