diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index bfd6227d8..ba25d4f95 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -523,15 +523,20 @@ bool BfCompiler::IsTypeAccessible(BfType* checkType, BfProject* curProject) BfTypeInstance* typeInst = checkType->ToTypeInstance(); if (typeInst != NULL) { - if (checkType->IsTuple()) + // A closure's type is its delegate's, from the delegate's project, so what it captures decides who can see it + if ((checkType->IsTuple()) || (checkType->IsClosure())) { for (auto&& fieldInst : typeInst->mFieldInstances) { - if (!IsTypeAccessible(fieldInst.mResolvedType, curProject)) + if ((fieldInst.mResolvedType != NULL) && (!IsTypeAccessible(fieldInst.mResolvedType, curProject))) return false; } } + // Nor does a closure carry its delegate's signature: that is its source delegate's + if ((checkType->IsClosure()) && (((BfClosureType*)typeInst)->mSrcDelegate != NULL) && (!IsTypeAccessible(((BfClosureType*)typeInst)->mSrcDelegate, curProject))) + return false; + auto genericTypeInst = typeInst->ToGenericTypeInstance(); if (genericTypeInst != NULL) { @@ -587,15 +592,18 @@ bool BfCompiler::IsTypeUsed(BfType* checkType, BfProject* curProject, bool conse if (checkType->IsUnspecializedType()) return typeInst->mIsReified || conservativeCheck; - if (checkType->IsTuple()) + if ((checkType->IsTuple()) || (checkType->IsClosure())) { for (auto&& fieldInst : typeInst->mFieldInstances) { - if (!IsTypeUsed(fieldInst.mResolvedType, curProject, true)) + if ((fieldInst.mResolvedType != NULL) && (!IsTypeUsed(fieldInst.mResolvedType, curProject, true))) return false; } } + if ((checkType->IsClosure()) && (((BfClosureType*)typeInst)->mSrcDelegate != NULL) && (!IsTypeUsed(((BfClosureType*)typeInst)->mSrcDelegate, curProject, true))) + return false; + auto genericTypeInst = typeInst->ToGenericTypeInstance(); if (genericTypeInst != NULL) { diff --git a/IDEHelper/Tests/LibA/src/LibA0.bf b/IDEHelper/Tests/LibA/src/LibA0.bf index c749440fe..f083fe835 100644 --- a/IDEHelper/Tests/LibA/src/LibA0.bf +++ b/IDEHelper/Tests/LibA/src/LibA0.bf @@ -235,3 +235,12 @@ static return 1; } } + +namespace SharedGlobals +{ + // LibA's part of the globals LibC also has globals in; TestsB calls it + static + { + public static int32 LibAGlobal() => 3; + } +} diff --git a/IDEHelper/Tests/LibC/src/LibCClosures.bf b/IDEHelper/Tests/LibC/src/LibCClosures.bf new file mode 100644 index 000000000..652aa7bae --- /dev/null +++ b/IDEHelper/Tests/LibC/src/LibCClosures.bf @@ -0,0 +1,21 @@ +using System; + +namespace SharedGlobals +{ + // Globals of a namespace LibA has globals in too: one globals type, its LibC part in a module + // of its own. TestsB, which uses LibA's part but does not reference LibC, must not take this + // part (and the closure in it, capturing a LibC type) into its link + struct LibCCaptured + { + public int32 mValue; + } + + static + { + public static int32 LibCCaptures(LibCCaptured captured) + { + delegate int32() get = scope () => captured.mValue; + return get(); + } + } +} diff --git a/IDEHelper/Tests/TestsB/src/TestsB0.bf b/IDEHelper/Tests/TestsB/src/TestsB0.bf index aec4c04d0..968077288 100644 --- a/IDEHelper/Tests/TestsB/src/TestsB0.bf +++ b/IDEHelper/Tests/TestsB/src/TestsB0.bf @@ -47,6 +47,10 @@ namespace TestsB int la0a = la0.GetA(); Test.Assert(la0a == 2); + // LibA's part of globals LibC has a part of too: LibC's part (a closure capturing a LibC type) + // must stay out of this executable, which does not reference LibC + Test.Assert(SharedGlobals.LibAGlobal() == 3); + // Same LibA5_Derived.GetVal body the Tests project links, but this executable sees no // extension of LibA5, so the same 'base.GetVal()' has to resolve to the original LibA.LibA5_Derived la5d = scope .(); diff --git a/IDEHelper/Tests/src/Lambdas.bf b/IDEHelper/Tests/src/Lambdas.bf index 07a28fe91..212652cd3 100644 --- a/IDEHelper/Tests/src/Lambdas.bf +++ b/IDEHelper/Tests/src/Lambdas.bf @@ -160,5 +160,15 @@ namespace Tests Test.Assert((getNewer() == 20) && (getOlder() == 10)); Test.Assert(CallWith(() => @y) == 20); } + + // LibC's part of the SharedGlobals globals holds a closure capturing a LibC type; TestsB, which + // uses LibA's part but does not reference LibC, links only if it leaves LibC's part out + [Test] + public static void TestClosuresOfAnotherProjectsTypes() + { + SharedGlobals.LibCCaptured captured = .() { mValue = 7 }; + Test.Assert(SharedGlobals.LibCCaptures(captured) == 7); + Test.Assert(SharedGlobals.LibAGlobal() == 3); + } } }