aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 15:22:23 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 15:22:23 +0000
commitf7f5df01c416a10e3eb9508a7a7150c792a8b4ca (patch)
tree9d1fc624c74c129b24fe8b8a6f921f50b799e09a /lib
parent09498bb7aab9182ba172aae1045ca895d4f422d4 (diff)
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 <noreply@anthropic.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/CodeGen/CodeGen.cc14
-rw-r--r--lib/CodeGen/FunctionGen.cc30
2 files changed, 38 insertions, 6 deletions
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<SourceUnit> &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<Definition> Definitions;
std::optional<size_t> 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<SourceUnit> &Sources) {
reportError("MAIN must be a FUNCTION with no parameters returning INTEGER");
}
} else if (auto *X = dynamic_cast<ExternDeclAST*>(S.get())) {
- Reserve(X->getProto()->getName(), true);
+ Reserve(X->getProto()->getName(), true, false, true);
} else if (dynamic_cast<IncludeObjStmtAST*>(S.get())) {
} else if (auto *E = dynamic_cast<EnumTypeDeclAST*>(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<ArgumentType> 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());