Skip to content

Commit

Permalink
Allow generic bounded generics and implement semantics for subtyping …
Browse files Browse the repository at this point in the history
…between generics

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=263658854
  • Loading branch information
al3623 authored and lauraharker committed Aug 16, 2019
1 parent 15b5c7b commit 406f482
Show file tree
Hide file tree
Showing 10 changed files with 661 additions and 96 deletions.
43 changes: 41 additions & 2 deletions src/com/google/javascript/jscomp/FunctionTypeBuilder.java
Expand Up @@ -45,6 +45,7 @@
import com.google.javascript.rhino.jstype.StaticTypedScope; import com.google.javascript.rhino.jstype.StaticTypedScope;
import com.google.javascript.rhino.jstype.TemplateType; import com.google.javascript.rhino.jstype.TemplateType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
Expand Down Expand Up @@ -755,11 +756,37 @@ private ImmutableList<TemplateType> buildTemplateTypesFromJSDocInfo(
if (infoTypeKeys.isEmpty() && infoTypeTransformations.isEmpty()) { if (infoTypeKeys.isEmpty() && infoTypeTransformations.isEmpty()) {
return ImmutableList.of(); return ImmutableList.of();
} }

// Temporarily bootstrap the template environment with unbound (unknown bound) template types
List<TemplateType> unboundedTemplates = new ArrayList<>();
for (Map.Entry<String, JSTypeExpression> entry : infoTypeKeys.entrySet()) {
TemplateType template = typeRegistry.createTemplateType(entry.getKey());
unboundedTemplates.add(template);
}
this.templateScope = typeRegistry.createScopeWithTemplates(templateScope, unboundedTemplates);

// Evaluate template type bounds with bootstrapped environment and reroute the bounds to these
ImmutableList.Builder<TemplateType> templates = ImmutableList.builder(); ImmutableList.Builder<TemplateType> templates = ImmutableList.builder();
HashMap<TemplateType, JSType> templatesToBounds = new HashMap<>();
for (Map.Entry<String, JSTypeExpression> entry : infoTypeKeys.entrySet()) { for (Map.Entry<String, JSTypeExpression> entry : infoTypeKeys.entrySet()) {
JSType typeBound = typeRegistry.evaluateTypeExpression(entry.getValue(), templateScope); JSType typeBound = typeRegistry.evaluateTypeExpression(entry.getValue(), templateScope);
templates.add(typeRegistry.createTemplateType(entry.getKey(), typeBound)); TemplateType template =
typeRegistry.getType(templateScope, entry.getKey()).toMaybeTemplateType();
if (template != null) {
templatesToBounds.put(template, typeBound);
} else {
templatesToBounds.put(
typeRegistry.createTemplateType(entry.getKey(), typeBound), typeBound);
}
}

for (Map.Entry<TemplateType, JSType> entry : templatesToBounds.entrySet()) {
TemplateType template = entry.getKey();
JSType bound = entry.getValue();
template.setBound(bound);
templates.add(template);
} }

for (Map.Entry<String, Node> entry : infoTypeTransformations.entrySet()) { for (Map.Entry<String, Node> entry : infoTypeTransformations.entrySet()) {
if (allowTypeTransformations) { if (allowTypeTransformations) {
templates.add( templates.add(
Expand All @@ -768,7 +795,17 @@ private ImmutableList<TemplateType> buildTemplateTypesFromJSDocInfo(
reportWarning(TEMPLATE_TRANSFORMATION_ON_CLASS, entry.getKey()); reportWarning(TEMPLATE_TRANSFORMATION_ON_CLASS, entry.getKey());
} }
} }
return templates.build();
ImmutableList<TemplateType> builtTemplates = templates.build();
for (TemplateType template : builtTemplates) {
if (template.containsCycle()) {
reportError(
RhinoErrorReporter.PARSE_ERROR,
"Cycle detected in inheritance chain of type " + template.getReferenceName());
}
}

return builtTemplates;
} }


/** Infer the template type from the doc info. */ /** Infer the template type from the doc info. */
Expand Down Expand Up @@ -1174,4 +1211,6 @@ public boolean mayHaveSingleThrow() {
return block.hasOneChild() && block.getFirstChild().isThrow(); return block.hasOneChild() && block.getFirstChild().isThrow();
} }
} }


} }
84 changes: 12 additions & 72 deletions src/com/google/javascript/jscomp/TypeValidator.java
Expand Up @@ -41,6 +41,7 @@
import com.google.javascript.jscomp.JsIterables.MaybeBoxedIterableOrAsyncIterable; import com.google.javascript.jscomp.JsIterables.MaybeBoxedIterableOrAsyncIterable;
import com.google.javascript.jscomp.parsing.parser.util.format.SimpleFormat; import com.google.javascript.jscomp.parsing.parser.util.format.SimpleFormat;
import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.AbstractDefaultValueVisitor;
import com.google.javascript.rhino.jstype.EnumElementType; import com.google.javascript.rhino.jstype.EnumElementType;
import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSType;
Expand All @@ -49,18 +50,15 @@
import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.NamedType; import com.google.javascript.rhino.jstype.NamedType;
import com.google.javascript.rhino.jstype.NoType;
import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.Property; import com.google.javascript.rhino.jstype.Property;
import com.google.javascript.rhino.jstype.Property.OwnedProperty; import com.google.javascript.rhino.jstype.Property.OwnedProperty;
import com.google.javascript.rhino.jstype.ProxyObjectType;
import com.google.javascript.rhino.jstype.TemplateType; import com.google.javascript.rhino.jstype.TemplateType;
import com.google.javascript.rhino.jstype.TemplateTypeMap; import com.google.javascript.rhino.jstype.TemplateTypeMap;
import com.google.javascript.rhino.jstype.TemplateTypeReplacer; import com.google.javascript.rhino.jstype.TemplateTypeReplacer;
import com.google.javascript.rhino.jstype.TemplatizedType; import com.google.javascript.rhino.jstype.TemplatizedType;
import com.google.javascript.rhino.jstype.UnionType; import com.google.javascript.rhino.jstype.UnionType;
import com.google.javascript.rhino.jstype.UnknownType; import com.google.javascript.rhino.jstype.UnknownType;
import com.google.javascript.rhino.jstype.Visitor;
import java.io.Serializable; import java.io.Serializable;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
Expand Down Expand Up @@ -1145,50 +1143,42 @@ void expectWellFormedTemplatizedType(Node n) {
} }
} }


class WellFormedTemplatizedTypeVerifier implements Visitor<Boolean> { class WellFormedTemplatizedTypeVerifier extends AbstractDefaultValueVisitor<Boolean> {
Node node; Node node;


WellFormedTemplatizedTypeVerifier(Node node) { WellFormedTemplatizedTypeVerifier(Node node) {
super(true);
this.node = node; this.node = node;
} }


@Override
public Boolean caseNoType(NoType type) {
return true;
}

@Override @Override
public Boolean caseEnumElementType(EnumElementType type) { public Boolean caseEnumElementType(EnumElementType type) {
return type.getPrimitiveType() != null ? type.getPrimitiveType().visit(this) : true; return type.getPrimitiveType() != null ? type.getPrimitiveType().visit(this) : true;
} }


@Override @Override
public Boolean caseFunctionType(FunctionType type) { public Boolean caseFunctionType(FunctionType type) {
boolean result = true;
for (JSType param : type.getParameterTypes()) { for (JSType param : type.getParameterTypes()) {
result &= param.visit(this); if (!param.visit(this)) {
return false;
}
} }
result &= type.getReturnType().visit(this); return type.getReturnType().visit(this);
return result;
} }


@Override @Override
public Boolean caseNamedType(NamedType type) { public Boolean caseNamedType(NamedType type) {
return type.getReferencedType().visit(this); return type.getReferencedType().visit(this);
} }


@Override
public Boolean caseProxyObjectType(ProxyObjectType type) {
return true;
}

@Override @Override
public Boolean caseUnionType(UnionType type) { public Boolean caseUnionType(UnionType type) {
boolean result = true;
for (JSType alt : type.getAlternates()) { for (JSType alt : type.getAlternates()) {
result &= alt.visit(this); if (!alt.visit(this)) {
return false;
}
} }
return result; return true;
} }


@Override @Override
Expand Down Expand Up @@ -1219,57 +1209,7 @@ public Boolean caseTemplatizedType(TemplatizedType type) {


@Override @Override
public Boolean caseTemplateType(TemplateType templateType) { public Boolean caseTemplateType(TemplateType templateType) {
return templateType.getBound().visit(this); return !templateType.containsCycle() && templateType.getBound().visit(this);
}

@Override
public Boolean caseObjectType(ObjectType type) {
return true;
}

@Override
public Boolean caseAllType() {
return true;
}

@Override
public Boolean caseBooleanType() {
return true;
}

@Override
public Boolean caseNoObjectType() {
return true;
}

@Override
public Boolean caseUnknownType() {
return true;
}

@Override
public Boolean caseNullType() {
return true;
}

@Override
public Boolean caseNumberType() {
return true;
}

@Override
public Boolean caseStringType() {
return true;
}

@Override
public Boolean caseSymbolType() {
return true;
}

@Override
public Boolean caseVoidType() {
return true;
} }
} }
} }
9 changes: 7 additions & 2 deletions src/com/google/javascript/jscomp/parsing/JsDocInfoParser.java
Expand Up @@ -1033,10 +1033,15 @@ private JsDocToken parseAnnotation(JsDocToken token,
token = templateInfo.token; token = templateInfo.token;
} else { } else {
List<String> genericDecl = Splitter.on("}").trimResults().splitToList(templateString); List<String> genericDecl = Splitter.on("}").trimResults().splitToList(templateString);
addParserWarning("msg.jsdoc.template.boundedgenerics"); addParserWarning(
"msg.jsdoc.template.boundedgenerics", templateLineno, templateCharno);


if (templateString.contains(",")) { if (templateString.contains(",")) {
addParserWarning("msg.jsdoc.template.multipletemplates"); if (!(templateString.indexOf('<') < templateString.indexOf(',')
&& templateString.indexOf('>') > templateString.indexOf(','))) {
addParserWarning(
"msg.jsdoc.template.multipletemplates", templateLineno, templateCharno);
}
} }


if (genericDecl.size() != 2) { if (genericDecl.size() != 2) {
Expand Down
@@ -0,0 +1,146 @@
/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;

/**
* A type visitor with a stored default value.<p>
*
* This code will provides an abstract base for other visitors
* in which most cases will simply return a default value.
*
* @author liuamanda@google.com (Amanda Liu)
*/
public abstract class AbstractDefaultValueVisitor<T> implements Visitor<T> {

private final T defaultValue;

public AbstractDefaultValueVisitor(T defaultValue) {
this.defaultValue = defaultValue;
}

@Override
public T caseNoType(NoType type) {
return defaultValue;
}

@Override
public T caseEnumElementType(EnumElementType type) {
return defaultValue;
}

@Override
public T caseAllType() {
return defaultValue;
}

@Override
public T caseBooleanType() {
return defaultValue;
}

@Override
public T caseNoObjectType() {
return defaultValue;
}

@Override
public T caseFunctionType(FunctionType type) {
return defaultValue;
}

@Override
public T caseObjectType(ObjectType type) {
return defaultValue;
}

@Override
public T caseUnknownType() {
return defaultValue;
}

@Override
public T caseNullType() {
return defaultValue;
}

@Override
public T caseNamedType(NamedType type) {
return defaultValue;
}

@Override
public T caseProxyObjectType(ProxyObjectType type) {
return defaultValue;
}

@Override
public T caseNumberType() {
return defaultValue;
}

@Override
public T caseStringType() {
return defaultValue;
}

@Override
public T caseSymbolType() {
return defaultValue;
}

@Override
public T caseVoidType() {
return defaultValue;
}

@Override
public T caseUnionType(UnionType type) {
return defaultValue;
}

@Override
public T caseTemplatizedType(TemplatizedType type) {
return defaultValue;
}

@Override
public T caseTemplateType(TemplateType templateType) {
return defaultValue;
}
}

0 comments on commit 406f482

Please sign in to comment.