mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Add FFlag
This commit is contained in:
parent
06e3cd43c1
commit
326193cd2e
2 changed files with 155 additions and 59 deletions
|
@ -47,6 +47,7 @@ LUAU_FASTFLAGVARIABLE(LuauCompleteVisitor, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReportShadowedTypeAlias, false)
|
LUAU_FASTFLAGVARIABLE(LuauReportShadowedTypeAlias, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBetterMessagingOnCountMismatch, false)
|
LUAU_FASTFLAGVARIABLE(LuauBetterMessagingOnCountMismatch, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauArgMismatchReportFunctionLocation, false)
|
LUAU_FASTFLAGVARIABLE(LuauArgMismatchReportFunctionLocation, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDeclareClassPrototype, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -523,7 +524,7 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A
|
||||||
prototype(scope, *typealias, subLevel);
|
prototype(scope, *typealias, subLevel);
|
||||||
++subLevel;
|
++subLevel;
|
||||||
}
|
}
|
||||||
else if (const auto& declaredClass = stat->as<AstStatDeclareClass>())
|
else if (const auto& declaredClass = stat->as<AstStatDeclareClass>(); FFlag::LuauDeclareClassPrototype && declaredClass)
|
||||||
{
|
{
|
||||||
prototype(scope, *declaredClass);
|
prototype(scope, *declaredClass);
|
||||||
}
|
}
|
||||||
|
@ -1633,6 +1634,8 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
||||||
|
|
||||||
void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauDeclareClassPrototype);
|
||||||
|
|
||||||
std::optional<TypeId> superTy = std::nullopt;
|
std::optional<TypeId> superTy = std::nullopt;
|
||||||
if (declaredClass.superName)
|
if (declaredClass.superName)
|
||||||
{
|
{
|
||||||
|
@ -1671,6 +1674,8 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& de
|
||||||
|
|
||||||
void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauDeclareClassPrototype)
|
||||||
|
{
|
||||||
Name className(declaredClass.name.value);
|
Name className(declaredClass.name.value);
|
||||||
|
|
||||||
// Don't bother checking if the class definition was incorrect
|
// Don't bother checking if the class definition was incorrect
|
||||||
|
@ -1692,6 +1697,94 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar
|
||||||
ice("No metatable for declared class");
|
ice("No metatable for declared class");
|
||||||
|
|
||||||
TableTypeVar* metatable = getMutable<TableTypeVar>(*ctv->metatable);
|
TableTypeVar* metatable = getMutable<TableTypeVar>(*ctv->metatable);
|
||||||
|
for (const AstDeclaredClassProp& prop : declaredClass.props)
|
||||||
|
{
|
||||||
|
Name propName(prop.name.value);
|
||||||
|
TypeId propTy = resolveType(scope, *prop.ty);
|
||||||
|
|
||||||
|
bool assignToMetatable = isMetamethod(propName);
|
||||||
|
Luau::ClassTypeVar::Props& assignTo = assignToMetatable ? metatable->props : ctv->props;
|
||||||
|
|
||||||
|
// Function types always take 'self', but this isn't reflected in the
|
||||||
|
// parsed annotation. Add it here.
|
||||||
|
if (prop.isMethod)
|
||||||
|
{
|
||||||
|
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(propTy))
|
||||||
|
{
|
||||||
|
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||||
|
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
||||||
|
ftv->hasSelf = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assignTo.count(propName) == 0)
|
||||||
|
{
|
||||||
|
assignTo[propName] = {propTy};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypeId currentTy = assignTo[propName].type;
|
||||||
|
|
||||||
|
// We special-case this logic to keep the intersection flat; otherwise we
|
||||||
|
// would create a ton of nested intersection types.
|
||||||
|
if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(currentTy))
|
||||||
|
{
|
||||||
|
std::vector<TypeId> options = itv->parts;
|
||||||
|
options.push_back(propTy);
|
||||||
|
TypeId newItv = addType(IntersectionTypeVar{std::move(options)});
|
||||||
|
|
||||||
|
assignTo[propName] = {newItv};
|
||||||
|
}
|
||||||
|
else if (get<FunctionTypeVar>(currentTy))
|
||||||
|
{
|
||||||
|
TypeId intersection = addType(IntersectionTypeVar{{currentTy, propTy}});
|
||||||
|
|
||||||
|
assignTo[propName] = {intersection};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reportError(declaredClass.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::optional<TypeId> superTy = std::nullopt;
|
||||||
|
if (declaredClass.superName)
|
||||||
|
{
|
||||||
|
Name superName = Name(declaredClass.superName->value);
|
||||||
|
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
||||||
|
|
||||||
|
if (!lookupType)
|
||||||
|
{
|
||||||
|
reportError(declaredClass.location, UnknownSymbol{superName, UnknownSymbol::Type});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't have generic classes, so this assertion _should_ never be hit.
|
||||||
|
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||||
|
superTy = lookupType->type;
|
||||||
|
|
||||||
|
if (!get<ClassTypeVar>(follow(*superTy)))
|
||||||
|
{
|
||||||
|
reportError(declaredClass.location, GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'",
|
||||||
|
superName.c_str(), declaredClass.name.value)});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Name className(declaredClass.name.value);
|
||||||
|
|
||||||
|
TypeId classTy = addType(ClassTypeVar(className, {}, superTy, std::nullopt, {}, {}, currentModuleName));
|
||||||
|
|
||||||
|
ClassTypeVar* ctv = getMutable<ClassTypeVar>(classTy);
|
||||||
|
TypeId metaTy = addType(TableTypeVar{TableState::Sealed, scope->level});
|
||||||
|
TableTypeVar* metatable = getMutable<TableTypeVar>(metaTy);
|
||||||
|
|
||||||
|
ctv->metatable = metaTy;
|
||||||
|
|
||||||
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
||||||
|
|
||||||
for (const AstDeclaredClassProp& prop : declaredClass.props)
|
for (const AstDeclaredClassProp& prop : declaredClass.props)
|
||||||
{
|
{
|
||||||
|
@ -1743,6 +1836,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFunction& global)
|
void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFunction& global)
|
||||||
|
|
|
@ -398,6 +398,8 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_string_props")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
|
TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag LuauDeclareClassPrototype("LuauDeclareClassPrototype", true);
|
||||||
|
|
||||||
unfreeze(typeChecker.globalTypes);
|
unfreeze(typeChecker.globalTypes);
|
||||||
LoadDefinitionFileResult result = loadDefinitionFile(typeChecker, typeChecker.globalScope, R"(
|
LoadDefinitionFileResult result = loadDefinitionFile(typeChecker, typeChecker.globalScope, R"(
|
||||||
declare class Channel
|
declare class Channel
|
||||||
|
|
Loading…
Add table
Reference in a new issue