• 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.nullness.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   @AutoValue
312   abstract class PrimTy implements Type {
313 
create(TurbineConstantTypeKind tykind, ImmutableList<AnnoInfo> annos)314     public static PrimTy create(TurbineConstantTypeKind tykind, ImmutableList<AnnoInfo> annos) {
315       return new AutoValue_Type_PrimTy(tykind, annos);
316     }
317 
318     /** The primtive type kind. */
primkind()319     public abstract TurbineConstantTypeKind primkind();
320 
321     @Override
tyKind()322     public TyKind tyKind() {
323       return TyKind.PRIM_TY;
324     }
325 
326     /** The type annotations. */
annos()327     public abstract ImmutableList<AnnoInfo> annos();
328 
329     @Override
toString()330     public final String toString() {
331       StringBuilder sb = new StringBuilder();
332       for (AnnoInfo anno : annos()) {
333         sb.append(anno);
334         sb.append(' ');
335       }
336       sb.append(primkind());
337       return sb.toString();
338     }
339 
340     @Memoized
341     @Override
hashCode()342     public abstract int hashCode();
343   }
344 
345   /** A wildcard type, valid only inside (possibly nested) type arguments. */
346   abstract class WildTy implements Type {
347 
348     public enum BoundKind {
349       NONE,
350       UPPER,
351       LOWER
352     }
353 
boundKind()354     public abstract BoundKind boundKind();
355 
bound()356     public abstract Type bound();
357 
358     /** The type annotations. */
annotations()359     public abstract ImmutableList<AnnoInfo> annotations();
360 
361     @Override
tyKind()362     public TyKind tyKind() {
363       return TyKind.WILD_TY;
364     }
365   }
366 
367   /** An upper-bounded wildcard type. */
368   @AutoValue
369   abstract class WildUpperBoundedTy extends WildTy {
370 
create(Type bound, ImmutableList<AnnoInfo> annotations)371     public static WildUpperBoundedTy create(Type bound, ImmutableList<AnnoInfo> annotations) {
372       return new AutoValue_Type_WildUpperBoundedTy(annotations, bound);
373     }
374 
375     /** The upper bound. */
376     @Override
bound()377     public abstract Type bound();
378 
379     @Override
boundKind()380     public BoundKind boundKind() {
381       return BoundKind.UPPER;
382     }
383 
384     @Override
toString()385     public final String toString() {
386       StringBuilder sb = new StringBuilder();
387       for (AnnoInfo anno : annotations()) {
388         sb.append(anno);
389         sb.append(' ');
390       }
391       sb.append("? extends ");
392       sb.append(bound());
393       return sb.toString();
394     }
395 
396     @Memoized
397     @Override
hashCode()398     public abstract int hashCode();
399   }
400 
401   /** An lower-bounded wildcard type. */
402   @AutoValue
403   abstract class WildLowerBoundedTy extends WildTy {
404 
create(Type bound, ImmutableList<AnnoInfo> annotations)405     public static WildLowerBoundedTy create(Type bound, ImmutableList<AnnoInfo> annotations) {
406       return new AutoValue_Type_WildLowerBoundedTy(annotations, bound);
407     }
408 
409     /** The lower bound. */
410     @Override
bound()411     public abstract Type bound();
412 
413     @Override
boundKind()414     public BoundKind boundKind() {
415       return BoundKind.LOWER;
416     }
417 
418     @Override
toString()419     public final String toString() {
420       StringBuilder sb = new StringBuilder();
421       for (AnnoInfo anno : annotations()) {
422         sb.append(anno);
423         sb.append(' ');
424       }
425       sb.append("? super ");
426       sb.append(bound());
427       return sb.toString();
428     }
429 
430     @Memoized
431     @Override
hashCode()432     public abstract int hashCode();
433   }
434 
435   /** An unbounded wildcard type. */
436   @AutoValue
437   abstract class WildUnboundedTy extends WildTy {
438 
create(ImmutableList<AnnoInfo> annotations)439     public static WildUnboundedTy create(ImmutableList<AnnoInfo> annotations) {
440       return new AutoValue_Type_WildUnboundedTy(annotations);
441     }
442 
443     @Override
boundKind()444     public BoundKind boundKind() {
445       return BoundKind.NONE;
446     }
447 
448     @Override
bound()449     public Type bound() {
450       throw new IllegalStateException();
451     }
452 
453     @Override
toString()454     public final String toString() {
455       StringBuilder sb = new StringBuilder();
456       for (AnnoInfo anno : annotations()) {
457         sb.append(anno);
458         sb.append(' ');
459       }
460       sb.append('?');
461       return sb.toString();
462     }
463 
464     @Memoized
465     @Override
hashCode()466     public abstract int hashCode();
467   }
468 
469   /** An intersection type. */
470   @AutoValue
471   abstract class IntersectionTy implements Type {
472 
bounds()473     public abstract ImmutableList<Type> bounds();
474 
create(ImmutableList<Type> bounds)475     public static IntersectionTy create(ImmutableList<Type> bounds) {
476       return new AutoValue_Type_IntersectionTy(bounds);
477     }
478 
479     @Override
tyKind()480     public TyKind tyKind() {
481       return TyKind.INTERSECTION_TY;
482     }
483 
484     @Memoized
485     @Override
hashCode()486     public abstract int hashCode();
487 
488     @Override
toString()489     public final String toString() {
490       return Joiner.on('&').join(bounds());
491     }
492   }
493 
494   /** A method type. */
495   @AutoValue
496   abstract class MethodTy implements Type {
497 
tyParams()498     public abstract ImmutableSet<TyVarSymbol> tyParams();
499 
returnType()500     public abstract Type returnType();
501 
502     /** The type of the receiver parameter (see JLS 8.4.1). */
receiverType()503     public abstract @Nullable Type receiverType();
504 
parameters()505     public abstract ImmutableList<Type> parameters();
506 
thrown()507     public abstract ImmutableList<Type> thrown();
508 
create( ImmutableSet<TyVarSymbol> tyParams, Type returnType, Type receiverType, ImmutableList<Type> parameters, ImmutableList<Type> thrown)509     public static MethodTy create(
510         ImmutableSet<TyVarSymbol> tyParams,
511         Type returnType,
512         Type receiverType,
513         ImmutableList<Type> parameters,
514         ImmutableList<Type> thrown) {
515       return new AutoValue_Type_MethodTy(tyParams, returnType, receiverType, parameters, thrown);
516     }
517 
518     @Override
tyKind()519     public TyKind tyKind() {
520       return TyKind.METHOD_TY;
521     }
522 
523     @Override
toString()524     public final String toString() {
525       StringBuilder sb = new StringBuilder();
526       if (!tyParams().isEmpty()) {
527         sb.append('<');
528         Joiner.on(',').appendTo(sb, tyParams());
529         sb.append('>');
530       }
531       sb.append('(');
532       Joiner.on(',').appendTo(sb, parameters());
533       sb.append(')');
534       sb.append(returnType());
535       return sb.toString();
536     }
537 
538     @Memoized
539     @Override
hashCode()540     public abstract int hashCode();
541   }
542 
543   /** An error type. */
544   final class ErrorTy implements Type {
545 
546     private final String name;
547 
ErrorTy(String name)548     private ErrorTy(String name) {
549       this.name = requireNonNull(name);
550     }
551 
552     /**
553      * Best-effort syntactic context for use in diagnostics or by annotation processors. This may be
554      * a simple or qualified name; it is not a canonical qualified name.
555      */
name()556     public String name() {
557       return name;
558     }
559 
create(Iterable<Tree.Ident> names)560     public static ErrorTy create(Iterable<Tree.Ident> names) {
561       List<String> bits = new ArrayList<>();
562       for (Tree.Ident ident : names) {
563         bits.add(ident.value());
564       }
565       return create(Joiner.on('.').join(bits));
566     }
567 
create(String name)568     public static ErrorTy create(String name) {
569       return new ErrorTy(name);
570     }
571 
572     @Override
tyKind()573     public TyKind tyKind() {
574       return TyKind.ERROR_TY;
575     }
576 
577     @Override
toString()578     public final String toString() {
579       return name();
580     }
581 
582     @Override
hashCode()583     public final int hashCode() {
584       return System.identityHashCode(this);
585     }
586 
587     @Override
equals(@ullable Object other)588     public final boolean equals(@Nullable Object other) {
589       // The name associated with an error type is context for use in diagnostics or by annotations
590       // processors. Two error types with the same name don't necessarily represent the same type.
591 
592       // TODO(cushon): should error types compare equal to themselves if they correspond to the same
593       // source location? Investigate storing the source position for this type, or replacing with
594       // `this == other` (and removing interning in ModelFactory).
595 
596       return false;
597     }
598   }
599 }
600