Skip to content

'Never' is not checked for unreachability #10973

Closed

Description

@tinganho
function fail(): never {
    throw new Error('oops');
}

function test1() {
    let str: string;
    fail();
    str = 'helloworld';  // No error. Should error with unreachable code.
}

function test2() {
    let str: string;
    if (Math.random() > 0.5) {
        str = 'helloworld';
    }
    else {
        fail();
    }
    let str2 = str + ''; // Error: str is used before being assigned. But should not error.
}

Activity

  1. yortus commented on Sep 18, 2016

    @yortus
    Contributor

    I raised this point in #9260 and Anders Hejlsberg (@ahejlsberg) responded in #9260 (comment) (see second half of comment).

  2. tinganho commented on Sep 18, 2016

    @tinganho
    ContributorAuthor

    Troy Gerwien (@yortus) your example which Anders Hejlsberg (@ahejlsberg) commented on is slightly different then this case. That example is for inferred 'never' typing for functions. In my example I use a declared 'never' function.

    Just quoting Anders Hejlsberg (@ahejlsberg) :

    We only report unreachable code errors based on the structure of the code, i.e. when we are 100% certain the code is unreachable in all circumstances.

    Maybe inferred cases cannot reach 100%. I don't know. But for me, this example is 100%:

    function test1() {
        let str: string;
        fail();
        str = 'helloworld';  // No error. Should error with unreachable code.
    }

    Because it is nearly exactly the same as this one:

    function test1() {
        let str: string;
        throw new Error('oops');
        str = 'helloworld';  // Error unreachable code detected
    }

    This is also 100%:

    function test2() {
        let str: string;
        if (Math.random() > 0.5) {
            str = 'helloworld';
        }
        else {
            fail();
        }
        let str2 = str + ''; // Error: str is used before being assigned. But should not error.
    }

    Because this one already are:

    function test2() {
        let str: string;
        if (Math.random() > 0.5) {
            str = 'helloworld';
        }
        else {
            throw new Error('oops');
        }
        let str2 = str + ''; // No error
    }
  3. yortus commented on Sep 18, 2016

    @yortus
    Contributor

    Tingan Ho (@tinganho) First of all, I would prefer the behaviour you are suggesting here (i.e. detect the unreachable code and error).

    That aside, I don't think the compiler currently does any cross-function analysis, so it doesn't assume the code after calling fail() is unreachable. So I suspect Anders Hejlsberg (@ahejlsberg)'s comment does apply to your example as well. It would be great if he could clarify it himself.

  4. ahejlsberg commented on Sep 18, 2016

    @ahejlsberg
    Member

    To clarify what I said here, reachability analysis considers only the grammatical structure of the code, i.e. it doesn't factor in types. So, we know for certain that control won't reach a statement following a throw or return, but we don't know for certain that a function that promises to never return won't actually return. The distinction is important when you for example consider that JavaScript code might violate type annotations by passing parameters or returning values that don't conform to the annotated types.

    That said, I agree it would be nice if we could report reachability errors following calls to never-returning functions, be they implicitly or explicitly typed. However, it's not a trivial change given how the analysis is currently structured. Meanwhile, you can make it work by writing return fail() instead of just fail():

    function fail(): never {
        throw new Error('oops');
    }
    
    function test1() {
        let str: string;
        return fail();
        str = 'helloworld';  // Error, unreachable code
    }
    
    function test2() {
        let str: string;
        if (Math.random() > 0.5) {
            str = 'helloworld';
        }
        else {
            return fail();
        }
        let str2 = str + '';  // Ok
    }
  5. tinganho commented on Sep 19, 2016

    @tinganho
    ContributorAuthor

    I think I'm starting to see a pattern here: never functions must be called in places where they can never be reached. Called elsewhere is not their intentions.

  6. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixedWorking as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions