1 module d.semantic.typepromotion;
2 
3 import d.semantic.semantic;
4 
5 import d.ir.symbol;
6 import d.ir.type;
7 
8 import source.location;
9 
10 import source.exception;
11 
12 // Conflict with Interface in object.di
13 alias Interface = d.ir.symbol.Interface;
14 
15 Type getPromotedType(SemanticPass pass, Location location, Type t1, Type t2) {
16 	return TypePromoter(pass, location, t1).visit(t2);
17 }
18 
19 // XXX: type promotion and finding common type are mixed up in there.
20 // This need to be splitted.
21 struct TypePromoter {
22 	// XXX: Used only to get to super class, should probably go away.
23 	private SemanticPass pass;
24 	alias pass this;
25 
26 	private Location location;
27 
28 	Type t1;
29 
30 	this(SemanticPass pass, Location location, Type t1) {
31 		this.pass = pass;
32 		this.location = location;
33 
34 		this.t1 = t1.getCanonical();
35 	}
36 
37 	Type visit(Type t) {
38 		return t.accept(this);
39 	}
40 
41 	Type visit(BuiltinType bt) {
42 		auto t = t1.getCanonicalAndPeelEnum();
43 
44 		if (bt == BuiltinType.Null) {
45 			if (t.kind == TypeKind.Pointer || t.kind == TypeKind.Class) {
46 				return t;
47 			}
48 
49 			if (t.kind == TypeKind.Function
50 				    && t.asFunctionType().contexts.length == 0) {
51 				return t;
52 			}
53 		}
54 
55 		if (t.kind == TypeKind.Builtin) {
56 			return Type.get(promoteBuiltin(bt, t.builtin));
57 		}
58 
59 		import std.conv;
60 		return getError(
61 			t,
62 			location,
63 			"Can't coerce " ~ bt.to!string() ~ " to " ~ t.toString(context),
64 		).type;
65 	}
66 
67 	Type visitPointerOf(Type t) {
68 		if (t1.kind == TypeKind.Builtin && t1.builtin == BuiltinType.Null) {
69 			return t.getPointer();
70 		}
71 
72 		if (t1.kind != TypeKind.Pointer) {
73 			assert(0, "Not Implemented.");
74 		}
75 
76 		auto e = t1.element;
77 		if (t.getCanonical().unqual() == e.getCanonical().unqual()) {
78 			if (canConvert(e.qualifier, t.qualifier)) {
79 				return t.getPointer();
80 			}
81 
82 			if (canConvert(t.qualifier, e.qualifier)) {
83 				return e.getPointer();
84 			}
85 		}
86 
87 		assert(0, "Not Implemented: use caster.");
88 	}
89 
90 	Type visitSliceOf(Type t) {
91 		assert(0, "Not Implemented.");
92 	}
93 
94 	Type visitArrayOf(uint size, Type t) {
95 		assert(0, "Not Implemented.");
96 	}
97 
98 	Type visit(Struct s) {
99 		if (t1.kind == TypeKind.Struct && t1.dstruct is s) {
100 			return Type.get(s);
101 		}
102 
103 		import source.exception;
104 		throw new CompileException(location, "Incompatible struct type "
105 			~ s.name.toString(context) ~ " and " ~ t1.toString(context));
106 	}
107 
108 	Type visit(Class c) {
109 		if (t1.kind == TypeKind.Builtin && t1.builtin == BuiltinType.Null) {
110 			return Type.get(c);
111 		}
112 
113 		if (t1.kind != TypeKind.Class) {
114 			assert(0, "Not Implemented.");
115 		}
116 
117 		auto r = t1.dclass;
118 
119 		// Find a common superclass.
120 		auto lup = c;
121 		do {
122 			// Avoid allocation when possible.
123 			if (r is lup) {
124 				return t1;
125 			}
126 
127 			auto rup = r.base;
128 			while (rup !is rup.base) {
129 				if (rup is lup) {
130 					return Type.get(rup);
131 				}
132 
133 				rup = rup.base;
134 			}
135 
136 			lup = lup.base;
137 		} while (lup !is lup.base);
138 
139 		// lup must be Object by now.
140 		return Type.get(lup);
141 	}
142 
143 	Type visit(Enum e) {
144 		return visit(e.type);
145 	}
146 
147 	Type visit(TypeAlias a) {
148 		return visit(a.type);
149 	}
150 
151 	Type visit(Interface i) {
152 		assert(0, "Not Implemented.");
153 	}
154 
155 	Type visit(Union u) {
156 		assert(0, "Not Implemented.");
157 	}
158 
159 	Type visit(Function f) {
160 		assert(0, "Not Implemented.");
161 	}
162 
163 	Type visit(Type[] seq) {
164 		assert(0, "Not Implemented.");
165 	}
166 
167 	Type visit(FunctionType f) {
168 		assert(0, "Not Implemented.");
169 	}
170 
171 	Type visit(Pattern p) {
172 		assert(0, "Not implemented.");
173 	}
174 
175 	import d.ir.error;
176 	Type visit(CompileError e) {
177 		return e.type;
178 	}
179 }
180 
181 private:
182 
183 BuiltinType getBuiltinBase(BuiltinType t) {
184 	if (t == BuiltinType.Bool) {
185 		return BuiltinType.Int;
186 	}
187 
188 	if (isChar(t)) {
189 		return integralOfChar(t);
190 	}
191 
192 	return t;
193 }
194 
195 BuiltinType promoteBuiltin(BuiltinType t1, BuiltinType t2) {
196 	t1 = getBuiltinBase(t1);
197 	t2 = getBuiltinBase(t2);
198 
199 	if (isIntegral(t1) && isIntegral(t2)) {
200 		import std.algorithm;
201 		return max(t1, t2, BuiltinType.Int);
202 	}
203 
204 	assert(0, "Not implemented.");
205 }
206 
207 unittest {
208 	with (BuiltinType) {
209 		foreach (t1; [Bool, Char, Wchar, Byte, Ubyte, Short, Ushort, Int]) {
210 			foreach (t2; [Bool, Char, Wchar, Byte, Ubyte, Short, Ushort, Int]) {
211 				assert(promoteBuiltin(t1, t2) == Int);
212 			}
213 		}
214 
215 		foreach (t1; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int,
216 		              Uint]) {
217 			foreach (t2; [Dchar, Uint]) {
218 				assert(promoteBuiltin(t1, t2) == Uint);
219 				assert(promoteBuiltin(t2, t1) == Uint);
220 			}
221 		}
222 
223 		foreach (t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int,
224 		             Uint, Long]) {
225 			assert(promoteBuiltin(t, Long) == Long);
226 			assert(promoteBuiltin(Long, t) == Long);
227 		}
228 
229 		foreach (t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int,
230 		             Uint, Long, Ulong]) {
231 			assert(promoteBuiltin(t, Ulong) == Ulong);
232 			assert(promoteBuiltin(Ulong, t) == Ulong);
233 		}
234 
235 		foreach (t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int,
236 		             Uint, Long, Ulong, Cent]) {
237 			assert(promoteBuiltin(t, Cent) == Cent);
238 			assert(promoteBuiltin(Cent, t) == Cent);
239 		}
240 
241 		foreach (t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int,
242 		             Uint, Long, Ulong, Cent, Ucent]) {
243 			assert(promoteBuiltin(t, Ucent) == Ucent);
244 			assert(promoteBuiltin(Ucent, t) == Ucent);
245 		}
246 	}
247 }