• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.type;
18 
19 import static com.google.common.collect.Iterables.getLast;
20 import static java.lang.Math.max;
21 import static java.util.Objects.requireNonNull;
22 
23 import com.google.auto.value.AutoValue;
24 import com.google.auto.value.extension.memoized.Memoized;
25 import com.google.common.base.Joiner;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.Iterables;
29 import com.google.turbine.binder.sym.ClassSymbol;
30 import com.google.turbine.binder.sym.TyVarSymbol;
31 import com.google.turbine.model.TurbineConstantTypeKind;
32 import com.google.turbine.tree.Tree;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.List;
36 import org.jspecify.annotations.Nullable;
37 
38 /** JLS 4 types. */
39 public interface Type {
40 
41   /** A type kind. */
42   enum TyKind {
43     /** A primitive type. */
44     PRIM_TY,
45     /**
46      * The void type.
47      *
48      * <p>It isn't actually a type in the spec, but it's included here for convenience.
49      */
50     VOID_TY,
51     /** A class type. */
52     CLASS_TY,
53     /** An array type. */
54     ARRAY_TY,
55     /** A type variable type. */
56     TY_VAR,
57     /** A wildcard type. */
58     WILD_TY,
59     /** An intersection type. */
60     INTERSECTION_TY,
61     /** A method type. */
62     METHOD_TY,
63 
64     ERROR_TY,
65     NONE_TY,
66   }
67 
68   /** The type kind. */
tyKind()69   TyKind tyKind();
70 
71   /** The void type. */
72   Type VOID =
73       new Type() {
74         @Override
75         public TyKind tyKind() {
76           return TyKind.VOID_TY;
77         }
78 
79         @Override
80         public final String toString() {
81           return "void";
82         }
83       };
84 
85   /** The void type. */
86   Type NONE =
87       new Type() {
88         @Override
89         public TyKind tyKind() {
90           return TyKind.NONE_TY;
91         }
92 
93         @Override
94         public final String toString() {
95           return "none";
96         }
97       };
98 
99   /**
100    * A class type.
101    *
102    * <p>Qualified types (e.g. {@code OuterClass<Foo>.InnerClass<Bar>}) are repesented as a list
103    * {@link SimpleClassTy}s (enclosing types first), each of which contains a {@link ClassSymbol}
104    * and an optional list of type arguments.
105    */
106   @AutoValue
107   abstract class ClassTy implements Type {
108 
109     /**
110      * The {@link ClassTy} for {@code java.lang.Object}. There's nothing special about this
111      * instance, it's just to avoid some boilerplate.
112      */
113     public static final ClassTy OBJECT = asNonParametricClassTy(ClassSymbol.OBJECT);
114 
115     /** The {@link ClassTy} for {@code java.lang.String}. */
116     public static final ClassTy STRING = asNonParametricClassTy(ClassSymbol.STRING);
117 
118     public static final ClassTy CLONEABLE = asNonParametricClassTy(ClassSymbol.CLONEABLE);
119     public static final ClassTy SERIALIZABLE = asNonParametricClassTy(ClassSymbol.SERIALIZABLE);
120 
121     /** Returns a {@link ClassTy} with no type arguments for the given {@link ClassSymbol}. */
asNonParametricClassTy(ClassSymbol i)122     public static ClassTy asNonParametricClassTy(ClassSymbol i) {
123       return ClassTy.create(
124           Arrays.asList(SimpleClassTy.create(i, ImmutableList.of(), ImmutableList.of())));
125     }
126 
classes()127     public abstract ImmutableList<SimpleClassTy> classes();
128 
create(Iterable<SimpleClassTy> classes)129     public static ClassTy create(Iterable<SimpleClassTy> classes) {
130       return new AutoValue_Type_ClassTy(ImmutableList.copyOf(classes));
131     }
132 
133     @Override
tyKind()134     public TyKind tyKind() {
135       return TyKind.CLASS_TY;
136     }
137 
138     /** The class symbol. */
sym()139     public ClassSymbol sym() {
140       return getLast(classes()).sym();
141     }
142 
143     @Override
toString()144     public final String toString() {
145       StringBuilder sb = new StringBuilder();
146       boolean first = true;
147       for (SimpleClassTy c : classes()) {
148         String binaryName = c.sym().binaryName();
149         if (!first) {
150           for (AnnoInfo anno : c.annos()) {
151             sb.append(anno);
152             sb.append(' ');
153           }
154           sb.append('.');
155           sb.append(binaryName, binaryName.lastIndexOf('$') + 1, binaryName.length());
156         } else {
157           int idx = max(binaryName.lastIndexOf('/'), binaryName.lastIndexOf('$')) + 1;
158           String name = binaryName.replace('/', '.').replace('$', '.');
159           sb.append(name, 0, idx);
160           for (AnnoInfo anno : c.annos()) {
161             sb.append(anno);
162             sb.append(' ');
163           }
164           sb.append(name, idx, name.length());
165         }
166         if (!c.targs().isEmpty()) {
167           sb.append('<');
168           Joiner.on(',').appendTo(sb, c.targs());
169           sb.append('>');
170         }
171         first = false;
172       }
173       return sb.toString();
174     }
175 
176     /** One element of a qualified {@link ClassTy}. */
177     @AutoValue
178     public abstract static class SimpleClassTy {
179 
create( ClassSymbol sym, ImmutableList<Type> targs, ImmutableList<AnnoInfo> annos)180       public static SimpleClassTy create(
181           ClassSymbol sym, ImmutableList<Type> targs, ImmutableList<AnnoInfo> annos) {
182         return new AutoValue_Type_ClassTy_SimpleClassTy(sym, targs, annos);
183       }
184 
185       /** The class symbol of the element. */
sym()186       public abstract ClassSymbol sym();
187 
188       /** The type arguments. */
targs()189       public abstract ImmutableList<Type> targs();
190 
191       /** The type annotations. */
annos()192       public abstract ImmutableList<AnnoInfo> annos();
193 
194       @Memoized
195       @Override
hashCode()196       public abstract int hashCode();
197     }
198 
199     @Memoized
200     @Override
hashCode()201     public int hashCode() {
202       return Iterables.getLast(classes()).hashCode();
203     }
204 
205     @Override
equals(@ullable Object obj)206     public final boolean equals(@Nullable Object obj) {
207       if (!(obj instanceof ClassTy)) {
208         return false;
209       }
210       ClassTy that = (ClassTy) obj;
211       int i = this.classes().size() - 1;
212       int j = that.classes().size() - 1;
213       for (; i >= 0 && j >= 0; i--, j--) {
214         if (!this.classes().get(i).equals(that.classes().get(j))) {
215           return false;
216         }
217       }
218       // don't rely on canonical form for simple class names
219       if (hasTargs(this.classes(), i) || hasTargs(that.classes(), j)) {
220         return false;
221       }
222       return true;
223     }
224 
hasTargs(ImmutableList<SimpleClassTy> classes, int idx)225     private static boolean hasTargs(ImmutableList<SimpleClassTy> classes, int idx) {
226       for (; idx >= 0; idx--) {
227         SimpleClassTy simple = classes.get(idx);
228         if (!simple.targs().isEmpty() || !simple.annos().isEmpty()) {
229           return true;
230         }
231       }
232       return false;
233     }
234   }
235 
236   /** An array type. */
237   @AutoValue
238   abstract class ArrayTy implements Type {
239 
create(Type elem, ImmutableList<AnnoInfo> annos)240     public static ArrayTy create(Type elem, ImmutableList<AnnoInfo> annos) {
241       return new AutoValue_Type_ArrayTy(elem, annos);
242     }
243 
244     /** The element type of the array. */
elementType()245     public abstract Type elementType();
246 
247     @Override
tyKind()248     public TyKind tyKind() {
249       return TyKind.ARRAY_TY;
250     }
251 
252     /** The type annotations. */
annos()253     public abstract ImmutableList<AnnoInfo> annos();
254 
255     @Override
toString()256     public final String toString() {
257       StringBuilder sb = new StringBuilder();
258       sb.append(elementType());
259       if (!annos().isEmpty()) {
260         sb.append(' ');
261         for (AnnoInfo anno : annos()) {
262           sb.append(anno);
263           sb.append(' ');
264         }
265       }
266       sb.append("[]");
267       return sb.toString();
268     }
269 
270     @Memoized
271     @Override
hashCode()272     public abstract int hashCode();
273   }
274 
275   /** A type variable. */
276   @AutoValue
277   abstract class TyVar implements Type {
278 
create(TyVarSymbol sym, ImmutableList<AnnoInfo> annos)279     public static TyVar create(TyVarSymbol sym, ImmutableList<AnnoInfo> annos) {
280       return new AutoValue_Type_TyVar(sym, annos);
281     }
282 
283     /** The type variable's symbol. */
sym()284     public abstract TyVarSymbol sym();
285 
286     @Override
tyKind()287     public TyKind tyKind() {
288       return TyKind.TY_VAR;
289     }
290 
291     @Override
toString()292     public final String toString() {
293       StringBuilder sb = new StringBuilder();
294       for (AnnoInfo anno : annos()) {
295         sb.append(anno);
296         sb.append(' ');
297       }
298       sb.append(sym().name());
299       return sb.toString();
300     }
301 
302     /** The type annotations. */
annos()303     public abstract ImmutableList<AnnoInfo> annos();
304 
305     @Memoized
306     @Override
hashCode()307     public abstract int hashCode();
308   }
309 
310   /** A primitive type. */
311   // TODO: cushon - consider renaming this, since it models things like String and null that can
312   // appear as constants and not primitives.
313   @AutoValue
314   abstract class PrimTy implements Type {
315 
create(TurbineConstantTypeKind tykind, ImmutableList<AnnoInfo> annos)316     public static PrimTy create(TurbineConstantTypeKind tykind, ImmutableList<AnnoInfo> annos) {
317       return new AutoValue_Type_PrimTy(tykind, annos);
318     }
319 
320     /** The primtive type kind. */
primkind()321     public abstract TurbineConstantTypeKind primkind();
322 
323     @Override
tyKind()324     public TyKind tyKind() {
325       return TyKind.PRIM_TY;
326     }
327 
328     /** The type annotations. */
annos()329     public abstract ImmutableList<AnnoInfo> annos();
330 
331     @Override
toString()332     public final String toString() {
333       StringBuilder sb = new StringBuilder();
334       for (AnnoInfo anno : annos()) {
335         sb.append(anno);
336         sb.append(' ');
337       }
338       sb.append(primkind());
339       return sb.toString();
340     }
341 
342     @Memoized
343     @Override
hashCode()344     public abstract int hashCode();
345   }
346 
347   /** A wildcard type, valid only inside (possibly nested) type arguments. */
348   abstract class WildTy implements Type {
349 
350     public enum BoundKind {
351       NONE,
352       UPPER,
353       LOWER
354     }
355 
boundKind()356     public abstract BoundKind boundKind();
357 
bound()358     public abstract Type bound();
359 
360     /** The type annotations. */
annotations()361     public abstract ImmutableList<AnnoInfo> annotations();
362 
363     @Override
tyKind()364     public TyKind tyKind() {
365       return TyKind.WILD_TY;
366     }
367   }
368 
369   /** An upper-bounded wildcard type. */
370   @AutoValue
371   abstract class WildUpperBoundedTy extends WildTy {
372 
create(Type bound, ImmutableList<AnnoInfo> annotations)373     public static WildUpperBoundedTy create(Type bound, ImmutableList<AnnoInfo> annotations) {
374       return new AutoValue_Type_WildUpperBoundedTy(annotations, bound);
375     }
376 
377     /** The upper bound. */
378     @Override
bound()379     public abstract Type bound();
380 
381     @Override
boundKind()382     public BoundKind boundKind() {
383       return BoundKind.UPPER;
384     }
385 
386     @Override
toString()387     public final String toString() {
388       StringBuilder sb = new StringBuilder();
389       for (AnnoInfo anno : annotations()) {
390         sb.append(anno);
391         sb.append(' ');
392       }
393       sb.append("? extends ");
394       sb.append(bound());
395       return sb.toString();
396     }
397 
398     @Memoized
399     @Override
hashCode()400     public abstract int hashCode();
401   }
402 
403   /** An lower-bounded wildcard type. */
404   @AutoValue
405   abstract class WildLowerBoundedTy extends WildTy {
406 
create(Type bound, ImmutableList<AnnoInfo> annotations)407     public static WildLowerBoundedTy create(Type bound, ImmutableList<AnnoInfo> annotations) {
408       return new AutoValue_Type_WildLowerBoundedTy(annotations, bound);
409     }
410 
411     /** The lower bound. */
412     @Override
bound()413     public abstract Type bound();
414 
415     @Override
boundKind()416     public BoundKind boundKind() {
417       return BoundKind.LOWER;
418     }
419 
420     @Override
toString()421     public final String toString() {
422       StringBuilder sb = new StringBuilder();
423       for (AnnoInfo anno : annotations()) {
424         sb.append(anno);
425         sb.append(' ');
426       }
427       sb.append("? super ");
428       sb.append(bound());
429       return sb.toString();
430     }
431 
432     @Memoized
433     @Override
hashCode()434     public abstract int hashCode();
435   }
436 
437   /** An unbounded wildcard type. */
438   @AutoValue
439   abstract class WildUnboundedTy extends WildTy {
440 
create(ImmutableList<AnnoInfo> annotations)441     public static WildUnboundedTy create(ImmutableList<AnnoInfo> annotations) {
442       return new AutoValue_Type_WildUnboundedTy(annotations);
443     }
444 
445     @Override
boundKind()446     public BoundKind boundKind() {
447       return BoundKind.NONE;
448     }
449 
450     @Override
bound()451     public Type bound() {
452       throw new IllegalStateException();
453     }
454 
455     @Override
toString()456     public final String toString() {
457       StringBuilder sb = new StringBuilder();
458       for (AnnoInfo anno : annotations()) {
459         sb.append(anno);
460         sb.append(' ');
461       }
462       sb.append('?');
463       return sb.toString();
464     }
465 
466     @Memoized
467     @Override
hashCode()468     public abstract int hashCode();
469   }
470 
471   /** An intersection type. */
472   @AutoValue
473   abstract class IntersectionTy implements Type {
474 
bounds()475     public abstract ImmutableList<Type> bounds();
476 
create(ImmutableList<Type> bounds)477     public static IntersectionTy create(ImmutableList<Type> bounds) {
478       return new AutoValue_Type_IntersectionTy(bounds);
479     }
480 
481     @Override
tyKind()482     public TyKind tyKind() {
483       return TyKind.INTERSECTION_TY;
484     }
485 
486     @Memoized
487     @Override
hashCode()488     public abstract int hashCode();
489 
490     @Override
toString()491     public final String toString() {
492       return Joiner.on('&').join(bounds());
493     }
494   }
495 
496   /** A method type. */
497   @AutoValue
498   abstract class MethodTy implements Type {
499 
tyParams()500     public abstract ImmutableSet<TyVarSymbol> tyParams();
501 
returnType()502     public abstract Type returnType();
503 
504     /** The type of the receiver parameter (see JLS 8.4.1). */
receiverType()505     public abstract @Nullable Type receiverType();
506 
parameters()507     public abstract ImmutableList<Type> parameters();
508 
thrown()509     public abstract ImmutableList<Type> thrown();
510 
create( ImmutableSet<TyVarSymbol> tyParams, Type returnType, Type receiverType, ImmutableList<Type> parameters, ImmutableList<Type> thrown)511     public static MethodTy create(
512         ImmutableSet<TyVarSymbol> tyParams,
513         Type returnType,
514         Type receiverType,
515         ImmutableList<Type> parameters,
516         ImmutableList<Type> thrown) {
517       return new AutoValue_Type_MethodTy(tyParams, returnType, receiverType, parameters, thrown);
518     }
519 
520     @Override
tyKind()521     public TyKind tyKind() {
522       return TyKind.METHOD_TY;
523     }
524 
525     @Override
toString()526     public final String toString() {
527       StringBuilder sb = new StringBuilder();
528       if (!tyParams().isEmpty()) {
529         sb.append('<');
530         Joiner.on(',').appendTo(sb, tyParams());
531         sb.append('>');
532       }
533       sb.append('(');
534       Joiner.on(',').appendTo(sb, parameters());
535       sb.append(')');
536       sb.append(returnType());
537       return sb.toString();
538     }
539 
540     @Memoized
541     @Override
hashCode()542     public abstract int hashCode();
543   }
544 
545   /** An error type. */
546   final class ErrorTy implements Type {
547 
548     private final String name;
549     private final ImmutableList<Type> targs;
550 
ErrorTy(String name, ImmutableList<Type> targs)551     private ErrorTy(String name, ImmutableList<Type> targs) {
552       this.name = requireNonNull(name);
553       this.targs = requireNonNull(targs);
554     }
555 
556     /**
557      * Best-effort syntactic context for use in diagnostics or by annotation processors. This may be
558      * a simple or qualified name; it is not a canonical qualified name.
559      */
name()560     public String name() {
561       return name;
562     }
563 
targs()564     public ImmutableList<Type> targs() {
565       return targs;
566     }
567 
create(Iterable<Tree.Ident> names, ImmutableList<Type> targs)568     public static ErrorTy create(Iterable<Tree.Ident> names, ImmutableList<Type> targs) {
569       List<String> bits = new ArrayList<>();
570       for (Tree.Ident ident : names) {
571         bits.add(ident.value());
572       }
573       return create(Joiner.on('.').join(bits), targs);
574     }
575 
create(String name, ImmutableList<Type> targs)576     public static ErrorTy create(String name, ImmutableList<Type> targs) {
577       return new ErrorTy(name, targs);
578     }
579 
580     @Override
tyKind()581     public TyKind tyKind() {
582       return TyKind.ERROR_TY;
583     }
584 
585     @Override
toString()586     public final String toString() {
587       StringBuilder sb = new StringBuilder();
588       sb.append(name());
589       if (!targs().isEmpty()) {
590         sb.append('<');
591         Joiner.on(',').appendTo(sb, targs());
592         sb.append('>');
593       }
594       return sb.toString();
595     }
596 
597     @Override
hashCode()598     public final int hashCode() {
599       return System.identityHashCode(this);
600     }
601 
602     @Override
equals(@ullable Object other)603     public final boolean equals(@Nullable Object other) {
604       // The name associated with an error type is context for use in diagnostics or by annotations
605       // processors. Two error types with the same name don't necessarily represent the same type.
606 
607       // TODO(cushon): should error types compare equal to themselves if they correspond to the same
608       // source location? Investigate storing the source position for this type, or replacing with
609       // `this == other` (and removing interning in ModelFactory).
610 
611       return false;
612     }
613   }
614 }
615