Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Control flow analysis for array construction#11432

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ahejlsberg merged 24 commits intomasterfromcontrolFlowArrays
Oct 14, 2016
Merged

Conversation

@ahejlsberg
Copy link
Member

@ahejlsbergahejlsberg commentedOct 6, 2016
edited
Loading

This PR introduces control flow analysis for array construction patterns that originate in an empty array literal (x = []) followed by some number ofx.push(value),x.unshift(value) orx[n] = value operations.

Alet,const, orvar variable declared with no type annotation and an initial value of[] is considered an implicitany[] variable. When an implicitany variable (see#11263) or implicitany[] variable is assigned an empty array literal[], each followingx.push(value),x.unshift(value) orx[n] = value operationevolves the type of the variable in accordance with what elements are added to it.

functionf1(){letx=[];x[0]=5;x[1]="hello";x[2]=true;returnx;// (string | number | boolean)[]}functionf2(){letx=[];x.push(5);x.push("hello");x.push(true);returnx;// (string | number | boolean)[]}functionf3(){letx=null;if(cond()){x=[];while(cond()){x.push("hello");}}returnx;// string[] | null}

An array type is evolved only by operations on the variable in which the evolving array type originated. When an evolving array type variable is referenced in an expression, its type is "fixed" and cannot be further evolved.

functionf4(){letx=[];// x has evolving array typex.push(5);lety=x;// y has fixed array typex.push("hello");// Oky.push("hello");// Error}

Similar to implicitany variables, control flow analysis is unable to determine the actual types of implicitany[] variables when they are referenced in nested functions. In such cases, the variables will appear to have typeany[] in the nested functions and errors will be reported for the references if --noImplicitAny is enabled.

functionf3(){letx=[];// Error: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.x.push(5);functiong(){x;// Error: Variable 'x' implicitly has an 'any[]' type.}}

normalser, svieira, sanex3339, weswigham, aluanhaddad, basarat, mpawelski, and elvircrn reacted with thumbs up emojisvieira, tinganho, sanex3339, aluanhaddad, and mpawelski reacted with hooray emojialitaheri and aluanhaddad reacted with heart emoji
@zenmumbler
Copy link

Will this affect a problem (#11082) I submitted earlier? This sounds like its different enough from my problem but wanted to check if there is any overlap, thanks!

returnresult;
}

// Maps from T to T and avoids allocation of all elements map to themselves
Copy link
Member

Choose a reason for hiding this comment

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

typo: of → if

@ahejlsberg
Copy link
MemberAuthor

@zenmumbler No, this PR doesn't affect the issue in#11082.

@ahejlsberg
Copy link
MemberAuthor

@mhegazy Want to take a look before I merge this?

Copy link
Member

@sandersnsandersn left a comment

Choose a reason for hiding this comment

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

Besides a few small changes, I also want to make sure we're OK with less localised errors in a series of functions with no return type annotations. In the example below, the error moves from the line with 'oops' on it to the last line.

functionf(){letx=[];x.push(12);x.push('oops');returnx;}functiong(){leta=g();a.push('oops 2');// shouldn't be allowedreturna;// g now has type (string | number)[]}letsum=0;for(constnofg()){sum+=n;// error, can't add (string | number) to number}

Right now, I think I'm on the side of fewer annotations, but I think we should know about the pitfalls of the new behaviour.

var widenArray = [null, undefined]; // error at "widenArray"
~~~~~~~~~~
!!! error TS7005: Variable 'widenArray' implicitly has an 'any[]' type.
var emptyArray = []; // error at "emptyArray"
Copy link
Member

Choose a reason for hiding this comment

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

Delete comment and consider deleting the entire line.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Yes, will fix.


declarefunctioncond():boolean;

functionf1(){
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to have meaningful names for these test cases

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I think we're fine with the short names.

x[1]="hello";
x[2]=true;
returnx;// (string | number | boolean)[]
}
Copy link
Member

Choose a reason for hiding this comment

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

what about a test case that case that doesx[0] = 5; x[0] = "hello"; x[0] = true? It would still return(string | number | boolean)[], right?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Yes, you'd get the same type. We don't look at the value of the index expression, so nothing gained by an additional test.

}
if(node.expression.kind===SyntaxKind.PropertyAccessExpression){
constpropertyAccess=<PropertyAccessExpression>node.expression;
if(isNarrowableOperand(propertyAccess.expression)&&propertyAccess.name.text==="push"){
Copy link
Member

Choose a reason for hiding this comment

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

what aboutunshift? I used unshift briefly when writing spread types. :)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Yup, we should supportunshift, uncommon as it may be.

}

function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) {
return node.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>node).initializer && isEmptyArrayLiteral((<VariableDeclaration>node).initializer) ||
Copy link
Member

Choose a reason for hiding this comment

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

can you break these lines up a bit more? They are really hard to read on github.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Sure.

return incomplete ? { flags: 0, type } : type;
}

// An evolving array type tracks the element types that have so far been seen in an
Copy link
Member

Choose a reason for hiding this comment

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

comment should be jsdoc formatted

exportinterfaceAnonymousTypeextendsObjectType{
target?:AnonymousType;// Instantiation target
mapper?:TypeMapper;// Instantiation mapper
elementType?:Type;// Element expressions of evolving array type
Copy link
Member

@sandersnsandersnOct 12, 2016
edited
Loading

Choose a reason for hiding this comment

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

why isn't AutoArrayType a new subtype of AnonymousType? I think it would make it easier to track the property that auto array types don't escape the dynamic scope ofgetFlowTypeOfReference.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I don't follow.autoArrayType is thedeclared type of an auto-inferredany[] and it's already used outside ofgetFlowTypeOfReference.

Copy link
Member

Choose a reason for hiding this comment

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

It's separate fromautoArrayType, whose type is actually TypeReference.

I'm talking about the comment at the top of the new code that says

Evolving array types are ultimately converted into manifest array types
and never escape the getFlowTypeOfReference function."

I was suggesting something like:

exportinterfaceAutoArrayTypeextendsAnonymousType{elementType?:Type;finalArrayType?:Type;}

And then having the new code that returns AnonymousType today return AutoArrayType instead.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Oh, I see. The issue with doing it that way is that we don't have aTypeFlags.EvolvingArrayType that would indicate an evolving array type (because we're out of flag bits). Instead, we distinguish by looking for theelementType property onAnonymousType, so it has to be part ofAnonymousType.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, that makes sense.

getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction);
}

// Return true if the given node is 'x' in an 'x.push(value)' operation.
Copy link
Member

Choose a reason for hiding this comment

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

should support unshift here too

visitedFlowCount = visitedFlowStart;
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(result, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
// When the reference is 'x' in an 'x.push(value)' or 'x[n] = value' operation, we give type
// 'any[]' to 'x' instead of using the type determed by control flow analysis such that new
Copy link
Member

Choose a reason for hiding this comment

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

typo:determined

@ahejlsberg
Copy link
MemberAuthor

With the latest commits, when an evolving array has nox.push(value),x.unshift(value) orx[n] = value operations inany control flow path leading to the current location, its type is implicitlyany[]. Thus, any reference to such an empty evolving array will produce an error with--noImplicitAny:

functionf1(){leta=[];if(cond()){a.push("hello");}a;// string[]}functionf2(){leta=[];a;// any[], error with --noImplicitAny}

Note that references to thelength property are always allowed since they don't depend on the element type of the array:

functionf3(){leta=[];while(a.length<10){a.push("test");}returna;// string[]}

@ahejlsberg
Copy link
MemberAuthor

@mhegazy Latest commits should solve the issue we discussed yesterday.

# Conflicts:#src/compiler/checker.ts
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.

Reviewers

@sandersnsandersnsandersn approved these changes

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

5 participants

@ahejlsberg@zenmumbler@sandersn@msftclas

[8]ページ先頭

©2009-2025 Movatter.jp