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

Strictly check callback parameters #18976

Merged
merged 6 commits into from
Oct 6, 2017
Merged

Conversation

ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented Oct 5, 2017

With this PR we fix checking of the return position of callback parameters in --strictFunctionTypes mode. Previously we'd never check this position strictly (because of #15104) but now we check it strictly (i.e. contravariantly) for callback parameters of non-methods. For example:

// Compile with --strictFunctionTypes
declare let f1: (cb: (x: Animal) => Animal) => void;
declare let f2: (cb: (x: Dog) => Dog) => void;
f1 = f2;  // Error, but previously wasn't
f2 = f1;  // Error

The way the fix works is that once we've decided to check a parameter position strictly, we avoid the code path for callback parameters added in #15104. Basically, when we're checking a parameter strictly, we don't need to make exceptions for callbacks.

Fixes #18963 (at least the parts that we can fix).

@ahejlsberg ahejlsberg requested review from sandersn and mhegazy October 5, 2017 21:03
@mhegazy
Copy link
Contributor

mhegazy commented Oct 5, 2017

@ahejlsberg last time we talked about disabling this code path for --strictFunctionTypes you mentioned we needed this to avoid excessive recursion for computing the variance digest, is that not an issue now?

@DanielRosenwasser
Copy link
Member

It feels strange that the following examples undergo a regression and you lose checking under strictFunctionTypes:

interface Animal {a: any}
interface Dog extends Animal {d: any}


namespace n1 {
    class Foo {
        static f1(x: Animal): Animal { throw "wat"; }
        static f2(x: Dog): Animal { throw "wat"; };
    }
    
    declare let f1: (cb: typeof Foo.f1) => void;
    declare let f2: (cb: typeof Foo.f2) => void;
    f1 = f2;
    f2 = f1; // errors outside strictFunctionTypes, not with it.
}

namespace n2 {
    type BivariantHack<Input, Output> = { foo(x: Input): Output }["foo"];
    
    declare let f1: (cb: BivariantHack<Animal, Animal>) => void;
    declare let f2: (cb: BivariantHack<Dog, Animal>) => void;
    f1 = f2;
    f2 = f1; // errors outside strictFunctionTypes, not with it.
}

At the very least, we should have tests demonstrating the behavior, but I think we can still tighten this up.

@ahejlsberg
Copy link
Member Author

@DanielRosenwasser It's because a callback parameter check (contravariant parameters, bivariant return type) sits halfway between a strict function type check (contravariant parameters, covariant return type) and a method type check (bivariant parameters, bivariant return type). I suppose we could say that a callback parameter check occurs only if the callback type isn't declared as method (which you really have to contort yourself to do, as your example demonstrates). I think it is largely immaterial though, it never really occurs in normal code.

@ahejlsberg
Copy link
Member Author

last time we talked about disabling this code path for --strictFunctionTypes you mentioned we needed this to avoid excessive recursion for computing the variance digest, is that not an issue now?

Not sure what I was talking about there, it shouldn't matter.

@DanielRosenwasser
Copy link
Member

While I think it wouldn't be super difficult to do that, we should acknowledge these cases and add them to our test suite, with and without strictFunctionTypes.

@ahejlsberg
Copy link
Member Author

@DanielRosenwasser @mhegazy With the latest commits I've reverted back to using the callback parameter code path in --strictFunctionTypes mode as well, but with a tighter check on the return type of the callback. This means that in --strictFunctionTypes mode we'll check function types loosely only when they originate in method or constructor declarations and aren't in a callback parameter position.

@ahejlsberg ahejlsberg merged commit b7e744a into master Oct 6, 2017
@ahejlsberg ahejlsberg deleted the strictCallbackParameters branch October 6, 2017 20:36
Copy link

@SlurpTheo SlurpTheo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why !callbackCheck instead of CallbackCheck.None === callbackCheck?

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

strictFunctionTypes has different behavior with parameter types and return types
5 participants