diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-30 14:23:32 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-30 14:23:32 +0000 |
| commit | 4cb0d9b196008bc55dbfcdac1f9211dbbc879895 (patch) | |
| tree | 0d95a2682ef26455483c4bb9222e1dff0d5c8e69 /lib/CodeGen | |
| parent | 63109537383fa58c6a1db95f275d6b5de63b0015 (diff) | |
feat(types): support implicit single-argument constructors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/CallGen.cc | 186 | ||||
| -rw-r--r-- | lib/CodeGen/ConstructorGen.cc | 13 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 10 | ||||
| -rw-r--r-- | lib/CodeGen/OperatorGen.cc | 69 | ||||
| -rw-r--r-- | lib/CodeGen/TemplateClone.cc | 1 | ||||
| -rw-r--r-- | lib/CodeGen/TypeCheckGen.cc | 38 |
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(); |
