Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion IDEHelper/Compiler/CeMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11223,7 +11223,9 @@ CeTypeInfo* CeMachine::GetTypeInfo(BfType* type)
}

mCeModule->PopulateType(typeInstance, BfPopulateType_DataAndMethods);
ceTypeInfo->mRevision = typeInstance->mRevision;
// A type reached through a cycle can still be declaring its methods: answer with what it has
// now, but don't keep that, or every later read this compile sees the partial list
ceTypeInfo->mRevision = (typeInstance->mDefineState >= BfTypeDefineState_DefinedAndMethodsSlotted) ? typeInstance->mRevision : -1;
for (auto& methodGroup : typeInstance->mMethodInstanceGroups)
{
if (methodGroup.mOnDemandKind == BfMethodOnDemandKind_NoDecl_AwaitingReference)
Expand Down
56 changes: 56 additions & 0 deletions IDEHelper/Tests/src/Comptime.bf
Original file line number Diff line number Diff line change
Expand Up @@ -875,5 +875,61 @@ namespace Tests
{
Test.Assert(cZeroLengthMemOps == 1);
}

/// Reads its base's methods while it is applied, the way a generator asks whether an
/// ancestor already declares a member
[AttributeUsage(.Class)]
struct ReadsBaseMethodsAttribute : Attribute, IComptimeTypeApply
{
[Comptime]
public void ApplyToType(Type type)
{
int count = 0;
for (let m in type.BaseType.GetMethods(.Public | .Instance | .DeclaredOnly))
count++;
Compiler.EmitTypeBody(type, scope $"public const int cBaseMethodsSeen = {count};\n");
}
}

/// Declaring Make populates CycleDerived, whose generator reads this type's methods before
/// they are all declared
class CycleBase
{
public int First => 1;
public CycleDerived Make() => null;
public int Second => 2;
}

/// CycleBase's members without the cycle
class CycleTwin
{
public int First => 1;
public Object Make() => null;
public int Second => 2;
}

[ReadsBaseMethods]
class CycleDerived : CycleBase
{
}

[Comptime]
static int CountDeclaredMethods(Type type)
{
int count = 0;
for (let m in type.GetMethods(.Public | .Instance | .DeclaredOnly))
count++;
return count;
}

// A type's methods read while it was still declaring them (through a cycle) were cached as
// they stood, so every later read in the compile saw that partial list
[Test]
public static void TestMethodsReadAfterACycle()
{
Test.Assert(CountDeclaredMethods(typeof(CycleTwin)) >= 3);
Test.Assert(CountDeclaredMethods(typeof(CycleBase)) == CountDeclaredMethods(typeof(CycleTwin)));
Test.Assert(CycleDerived.cBaseMethodsSeen >= 0);
}
}
}
Loading