Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocompleting OO-Style methods is not working in the new type solver. #1239

Open
karl-police opened this issue Apr 26, 2024 · 13 comments
Open
Labels
bug Something isn't working

Comments

@karl-police
Copy link

karl-police commented Apr 26, 2024

My testing code that I put inside Autocomplete.test.cpp

TEST_CASE_FIXTURE(ACBuiltinsFixture, "idk2")
{
    // Change this to "false" to compare it to the old version
    ScopedFastFlag sff[]{
        {FFlag::DebugLuauDeferredConstraintResolution, true},
    };

    auto check1 = check(R"(
local name = {}
name.__index = name

function name.new(abc)
	local obj = setmetatable({}, name)
	obj.a = abc
	
	return obj
end


function name:test()
	self.b = "no"
end


local newClass = name.new("e")
newClass:@1
)");

    auto ac1 = autocomplete('1');
    CHECK(ac1.entryMap.count("test"));
}

Description

As you see DebugLuauDeferredConstraintResolution is enabled here. And what happens is that CHECK(ac1.entryMap.count("test")) fails due to that.

I tried to figure out why, and figured out, sort of a cause, but not an exact one, because there's too much going on. I wish I found more.

It has to do with this, though I am surprised to have never noticed this, but I barely test with DebugLuauDeferredConstraintResolution luau-lang/site@a48d849

 
Cause starts here:

if (FFlag::DebugLuauDeferredConstraintResolution)

If DebugLuauDeferredConstraintResolution is enabled, it will use Luau::check, (which is where the issue is at)

luau/Analysis/src/Frontend.cpp

Lines 1381 to 1384 in 259e509

return Luau::check(sourceModule, mode, requireCycles, builtinTypes, NotNull{&iceHandler},
NotNull{forAutocomplete ? &moduleResolverForAutocomplete : &moduleResolver}, NotNull{fileResolver},
environmentScope ? *environmentScope : globals.globalScope, prepareModuleScopeWrap, options, typeCheckLimits, recordJsonLog,
writeJsonLog);

If it's not enabled, it will use the previous typeChecker

return typeChecker.check(sourceModule, mode, environmentScope);

Now... I know, that this is literally what DebugLuauDeferredConstraintResolution is about... But that's what I figured out.

 

Expected Result

You will get :test

Actual Result

It seems like that only functions are affected, for some reason. You're forced to give the function type syntax... Generic types however won't work either. However, properties seem to recognise the type from generic types, just not functions within that one class table.

 

 

 

Research

I tried to find workarounds. I combo'd it like so as example:
image

But I had to comment this out
https://github.com/luau-lang/luau/blob/master/Analysis/src/Unifier.cpp#L407

But that just uses the old type checker....... but it's at that area, uhhh.... probably not really a special find. But Unifier supported it.

I don't even know how this is failing, it works fine outside that test. Unsure, I am probably enabling fast flags wrong.
image

@karl-police karl-police added the bug Something isn't working label Apr 26, 2024
@karl-police
Copy link
Author

karl-police commented Apr 26, 2024

Maybe this comment tells more.

image

@karl-police
Copy link
Author

It's because abc doesn't receive a type. It receives a freetype but idk, it needs to be a non-free type for this to be fixed. This is very very confusing.

HOW

does obj.a = abc receive the type string but the argument itself not...

@karl-police
Copy link
Author

karl-police commented Apr 26, 2024

Hi @karl-police ! I noticed in your test you wrote:

local newClass = name.new("e")
newClass:@1

In this case, name.new passes "e" as the receiver of the new method. name:new("abc") is effectively name.new(self, "abc"). When I replace name.new with name:new, this test works.

There's an issue here though.

I did not write name.new(self, abc) the defined function is name.new(abc) as the constructor.

Doing name:new in this case, is going to lead to bad results. The fact that the it somehow makes it through the type check is interesting.

@karl-police
Copy link
Author

karl-police commented Apr 26, 2024

I found out that the issue could be somewhere around here

TypeId argTy = nullptr;
if (local->annotation)
argTy = resolveType(signatureScope, local->annotation, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true);
else
{
if (i < expectedArgPack.head.size())
argTy = expectedArgPack.head[i];
else
argTy = freshType(signatureScope);
}

freshTypes work but it's very dank I do not understand how I am supposed to keep track of changes made because it's all dank, help

 

That's actually the only issue. If I can understand how this free fresh type gets modified to an actual type e.g. obj.a getting string,

BUT I DO NOT understand how it gets it, because I don't know how it works, there's like a bunch of AST and pointers and idk.

The type of abc is a in this case, idk what that should be

@karl-police
Copy link
Author

karl-police commented Apr 26, 2024

I don't know what happens to the type of abc, I see that a always gets string, uhhh...

I don't know how I can check the value of abc after the check result properly

@Vighnesh-V
Copy link
Collaborator

Vighnesh-V commented Apr 26, 2024

Hi! I think this is a bug on our end. We are working on making sure the experimental new type solver (under the DebugLuauDeferredConstraintResolution) is more stable, but it is not guaranteed to be at parity with the old solver. I have filed this as a bug and we will fix it at our earliest convenience. @karl-police Is there a particular reason you need the old new solver?

@karl-police
Copy link
Author

Hi! I think this is a bug on our end. We are working on making sure the experimental new type solver (under the DebugLuauDeferredConstraintResolution) is more stable, but it is not guaranteed to be at parity with the old solver. I have filed this as a bug and we will fix it at our earliest convenience. @karl-police Is there a particular reason you need the old solver?

I was just testing a few things. I wanted to combine types together again, I was thinking about making an RFC for variadic generic types, because U... doesn't do anything, not sure.

Then I noticed that something wasn't right with how the autocomplete works, and this is concerning. If THIS doesn't work, then what else might broke from it. That's about that.

@karl-police
Copy link
Author

karl-police commented Apr 26, 2024

Hi! I think this is a bug on our end. We are working on making sure the experimental new type solver (under the DebugLuauDeferredConstraintResolution) is more stable, but it is not guaranteed to be at parity with the old solver. I have filed this as a bug and we will fix it at our earliest convenience. @karl-police Is there a particular reason you need the old new solver?

Oh, you edited the old to new.

I don't seek the new solver. I was just using the new solver to see if a future update will break the old stuff. Break, in sense of "heavily".

keyof is cool, but I don't seek it.

I was curious on how I could figure out how to fix this bug here.

But I have not enough knowledge about all of this. Way too many breakpoints, there's gotta be something more efficient.

 

Maybe ConstraintSolver.cpp does it, there's a part where I do see "e" with upperBounds and lowerBounds, though idk how to use AST, maybe that can be used to get the function, then the argument and then somehow override the abc's type or something.

@aatxe aatxe changed the title Previous Autocomplete functionality was broken by DebugLuauDeferredConstraintResolution and Frontend.cpp through Luau::check for not using Unifier Autocompleting OO-Style methods is not working in the new type solver. May 4, 2024
@aatxe
Copy link
Collaborator

aatxe commented May 4, 2024

The sort of questions you're asking in trying to investigate the issue yourself are very architectural ones, and we don't have completed documentation available for the new solver since it is still in production, but in general, free types are how inference actually works, and most things (that do not have immediately determinable types) will be initially assigned a free type. Type inference is implemented between constraint generation (ConstraintGenerator.cpp) and constraint resolution (ConstraintSolver.cpp) where the former produces a pool of constraints along with types (including mostly the initial free types) for everything, and then the latter resolves those constraints to solve the types for the whole program. Free types, in general, have bounds that grow in unification (Unifier2.cpp), and are eventually generalized to their bounds when they have been completely solved.

@karl-police
Copy link
Author

karl-police commented May 16, 2024

Made a post here, where I just figured out something else

#1257

local test = {}
test.__index = test


function test.new(input: string)
	local self = setmetatable({}, test)
	
	self.name = input
	return self
end

function test:Get()
	return self.name
end

local newTest = test.new("test")

newTest:

This is where :Get() currently shows up, thanks to function test.new(input: string)

But if you mix it with function test.new<free>(input: string)

free is not assigned anywhere, yet will destroy :Get()

But there's something that <free> is doing, and that just reminded me about this issue.

local test = {}
test.__index = test


function test.new<free>()
	local self = setmetatable({}, test)
	
	self.emptyTable = {}
	
	return self
end

function test:Get()
	return "nothing"
end

local newTest = test.new()

newTest.emptyTable.AutoComplete = "test"

newTest.emptyTable.

Here, <free> is completly unused, there's no function arguments. However, newTest is now a completly free table. :Get() still doesn't show up in the autocomplete however.

But I can't explain how <free> just magically causes newTest.emptyTable. to autocomplete the contents inside of it.

 

I haven't checked Unifier2.cpp. But it sounds like it fails to merge some content and the object's table. Point is, <free> alone causes this issue already, and I think that's a very good lead to find where the issue is.

@karl-police
Copy link
Author

I looked at ConstraintSolver while debugging.

I finally found "test" now I can eventually figure out how it ends up going missing

image

@karl-police
Copy link
Author

idk what this is

image

idk how test lands in there, nor do I know where I am at in the ConstraintSolver

@karl-police
Copy link
Author

So, when it's not broken, this is the index
image

When it is broken, it has a different index, so I am not sure why that may be and what that would mean
image

But that info is not useful, because I still haven't found the place where it decides to put it in the entryMap

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

3 participants