From f7f5df01c416a10e3eb9508a7a7150c792a8b4ca Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 15:22:23 +0000 Subject: feat(codegen): allow identical EXTERN redeclarations across files Repeated EXTERN declarations with the same signature share one C symbol, as C declarations do; conflicting signatures are errors. Co-Authored-By: Claude Opus 5.5 --- lib/CodeGen/CodeGen.cc | 14 +++++++++----- lib/CodeGen/FunctionGen.cc | 30 +++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 7abdad7..61fd837 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -187,14 +187,16 @@ void CodeGen::compile(const std::vector &Sources) { }); FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); }); - struct Definition { size_t Unit; bool IsFunction; bool IsOverload; }; + struct Definition { size_t Unit; bool IsFunction; bool IsOverload; bool IsExtern; }; IdentifierMap Definitions; std::optional CodeUnit; FunctionDefAST *Entry = nullptr; size_t EntryUnit = 0; - auto Reserve = [&](const std::string &Name, bool IsFunction = false, bool IsOverload = false) { - auto [It, Added] = Definitions.emplace(Name, Definition{CurrentUnit, IsFunction, IsOverload}); - bool Compatible = IsFunction && IsOverload && It->second.IsFunction && It->second.IsOverload; + auto Reserve = [&](const std::string &Name, bool IsFunction = false, bool IsOverload = false, + bool IsExtern = false) { + auto [It, Added] = Definitions.emplace(Name, Definition{CurrentUnit, IsFunction, IsOverload, IsExtern}); + bool Compatible = (IsFunction && IsOverload && It->second.IsFunction && It->second.IsOverload) || + (IsExtern && It->second.IsExtern); if (!Added && It->second.Unit != CurrentUnit && !Compatible) reportError("Duplicate definition %s in %s and %s", Name.c_str(), Sources[It->second.Unit].Path.c_str(), Sources[CurrentUnit].Path.c_str()); @@ -216,7 +218,7 @@ void CodeGen::compile(const std::vector &Sources) { reportError("MAIN must be a FUNCTION with no parameters returning INTEGER"); } } else if (auto *X = dynamic_cast(S.get())) { - Reserve(X->getProto()->getName(), true); + Reserve(X->getProto()->getName(), true, false, true); } else if (dynamic_cast(S.get())) { } else if (auto *E = dynamic_cast(S.get())) { TypeName = E->getName(); @@ -461,6 +463,8 @@ const FuncSig *CodeGen::resolveCall(const std::string &Name, return nullptr; } if (Overloads.size() == 1 && !Overloads.front()->IsOverload) return Overloads.front(); + // Visible EXTERN redeclarations are identical by construction. + if (Overloads.front()->IsExternal) return Overloads.front(); struct ArgumentType { const TypeInfo *Type; int Rank; }; std::vector Actual; std::string Key; diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 3937e01..9559717 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -194,6 +194,10 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { FunctionType *FT = FunctionType::get(RetType, ArgTypes, false); auto &Overloads = Signatures[Sig.Name]; for (const FuncSig &Existing : Overloads) { + if (Existing.IsExternal) { + reportError("Function %s is already declared EXTERN", Proto->getName().c_str()); + return nullptr; + } if (!Sig.IsOverload || !Existing.IsOverload) { reportError("Function %s cannot be redefined. Multiple definition of %s; every overload must be declared OVERLOAD", Proto->getName().c_str(), Proto->getName().c_str()); @@ -304,12 +308,36 @@ bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { if (Attribute::AttrKind K = Extension(Info); K != Attribute::None) RetAttrs.addAttribute(K); } + // Repeated identical EXTERN declarations, as in C, share one symbol; each + // keeps its own unit so visibility follows the declaring file. auto &Overloads = Signatures[Sig.Name]; + if (!Overloads.empty() && Overloads.front().IsExternal) { + const FuncSig &First = Overloads.front(); + auto Same = [&](const std::string &A, const std::string &B) { + if (sameIdentifier(A, "VOID") || sameIdentifier(B, "VOID")) + return sameIdentifier(A, B); + const TypeInfo *L = Types.resolve(A); + return L && L->sameType(Types.resolve(B)); + }; + bool Matches = First.IsVariadic == Sig.IsVariadic && First.Params.size() == Sig.Params.size() && + Same(First.ReturnTypeName, Sig.ReturnTypeName); + for (size_t I = 0; Matches && I < Sig.Params.size(); ++I) + Matches = Same(First.Params[I].TypeName, Sig.Params[I].TypeName); + if (!Matches) { + reportError("Conflicting EXTERN declarations of %s", Name.c_str()); + return false; + } + Sig.ExternType = First.ExternType; + Sig.ExternAttrs = First.ExternAttrs; + PrototypeLinks.emplace(Proto, Name); + Overloads.push_back(std::move(Sig)); + return true; + } if (!Overloads.empty()) { if (Overloads.front().OwnerUnit != CurrentUnit) reportError("Duplicate definition %s in %s and %s", Name.c_str(), UnitPaths[Overloads.front().OwnerUnit].c_str(), UnitPaths[CurrentUnit].c_str()); - else reportError("Function %s cannot be redefined", Name.c_str()); + else reportError("EXTERN %s conflicts with FUNCTION/PROCEDURE %s", Name.c_str(), Name.c_str()); return false; } Sig.ExternType = FunctionType::get(RetType, ArgTypes, Proto->isVariadic()); -- cgit v1.3.1-4-g156e