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 && t.asFunctionType().contexts.length == 0) {
50 				return t;
51 			}
52 		}
53 		
54 		if (t.kind == TypeKind.Builtin) {
55 			return Type.get(promoteBuiltin(bt, t.builtin));
56 		}
57 		
58 		import std.conv;
59 		return getError(
60 			t,
61 			location,
62 			"Can't coerce " ~ bt.to!string() ~ " to " ~ t.toString(context),
63 		).type;
64 	}
65 	
66 	Type visitPointerOf(Type t) {
67 		if (t1.kind == TypeKind.Builtin && t1.builtin == BuiltinType.Null) {
68 			return t.getPointer();
69 		}
70 		
71 		if (t1.kind != TypeKind.Pointer) {
72 			assert(0, "Not Implemented.");
73 		}
74 		
75 		auto e = t1.element;
76 		if (t.getCanonical().unqual() == e.getCanonical().unqual()) {
77 			if (canConvert(e.qualifier, t.qualifier)) {
78 				return t.getPointer();
79 			}
80 			
81 			if (canConvert(t.qualifier, e.qualifier)) {
82 				return e.getPointer();
83 			}
84 		}
85 		
86 		assert(0, "Not Implemented: use caster.");
87 	}
88 	
89 	Type visitSliceOf(Type t) {
90 		assert(0, "Not Implemented.");
91 	}
92 	
93 	Type visitArrayOf(uint size, Type t) {
94 		assert(0, "Not Implemented.");
95 	}
96 	
97 	Type visit(Struct s) {
98 		if (t1.kind == TypeKind.Struct && t1.dstruct is s) {
99 			return Type.get(s);
100 		}
101 		
102 		import source.exception;
103 		throw new CompileException(location, "Incompatible struct type " ~ s.name.toString(context) ~ " and " ~ t1.toString(context));
104 	}
105 	
106 	Type visit(Class c) {
107 		if (t1.kind == TypeKind.Builtin && t1.builtin == BuiltinType.Null) {
108 			return Type.get(c);
109 		}
110 		
111 		if (t1.kind != TypeKind.Class) {
112 			assert(0, "Not Implemented.");
113 		}
114 		
115 		auto r = t1.dclass;
116 		
117 		// Find a common superclass.
118 		auto lup = c;
119 		do {
120 			// Avoid allocation when possible.
121 			if (r is lup) {
122 				return t1;
123 			}
124 			
125 			auto rup = r.base;
126 			while(rup !is rup.base) {
127 				if(rup is lup) {
128 					return Type.get(rup);
129 				}
130 				
131 				rup = rup.base;
132 			}
133 			
134 			lup = lup.base;
135 		} while(lup !is lup.base);
136 		
137 		// lup must be Object by now.
138 		return Type.get(lup);
139 	}
140 	
141 	Type visit(Enum e) {
142 		return visit(e.type);
143 	}
144 	
145 	Type visit(TypeAlias a) {
146 		return visit(a.type);
147 	}
148 	
149 	Type visit(Interface i) {
150 		assert(0, "Not Implemented.");
151 	}
152 	
153 	Type visit(Union u) {
154 		assert(0, "Not Implemented.");
155 	}
156 	
157 	Type visit(Function f) {
158 		assert(0, "Not Implemented.");
159 	}
160 	
161 	Type visit(Type[] seq) {
162 		assert(0, "Not Implemented.");
163 	}
164 	
165 	Type visit(FunctionType f) {
166 		assert(0, "Not Implemented.");
167 	}
168 	
169 	Type visit(Pattern p) {
170 		assert(0, "Not implemented.");
171 	}
172 	
173 	import d.ir.error;
174 	Type visit(CompileError e) {
175 		return e.type;
176 	}
177 }
178 
179 private:
180 
181 BuiltinType getBuiltinBase(BuiltinType t) {
182 	if (t == BuiltinType.Bool) {
183 		return BuiltinType.Int;
184 	}
185 	
186 	if (isChar(t)) {
187 		return integralOfChar(t);
188 	}
189 	
190 	return t;
191 }
192 
193 BuiltinType promoteBuiltin(BuiltinType t1, BuiltinType t2) {
194 	t1 = getBuiltinBase(t1);
195 	t2 = getBuiltinBase(t2);
196 	
197 	if (isIntegral(t1) && isIntegral(t2)) {
198 		import std.algorithm;
199 		return max(t1, t2, BuiltinType.Int);
200 	}
201 	
202 	assert(0, "Not implemented.");
203 }
204 
205 unittest { with(BuiltinType) {
206 	foreach(t1; [Bool, Char, Wchar, Byte, Ubyte, Short, Ushort, Int]) {
207 		foreach(t2; [Bool, Char, Wchar, Byte, Ubyte, Short, Ushort, Int]) {
208 			assert(promoteBuiltin(t1, t2) == Int);
209 		}
210 	}
211 	
212 	foreach(t1; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int, Uint]) {
213 		foreach(t2; [Dchar, Uint]) {
214 			assert(promoteBuiltin(t1, t2) == Uint);
215 			assert(promoteBuiltin(t2, t1) == Uint);
216 		}
217 	}
218 	
219 	foreach(t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int, Uint, Long]) {
220 		assert(promoteBuiltin(t, Long) == Long);
221 		assert(promoteBuiltin(Long, t) == Long);
222 	}
223 	
224 	foreach(t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int, Uint, Long, Ulong]) {
225 		assert(promoteBuiltin(t, Ulong) == Ulong);
226 		assert(promoteBuiltin(Ulong, t) == Ulong);
227 	}
228 	
229 	foreach(t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int, Uint, Long, Ulong, Cent]) {
230 		assert(promoteBuiltin(t, Cent) == Cent);
231 		assert(promoteBuiltin(Cent, t) == Cent);
232 	}
233 	
234 	foreach(t; [Bool, Char, Wchar, Dchar, Byte, Ubyte, Short, Ushort, Int, Uint, Long, Ulong, Cent, Ucent]) {
235 		assert(promoteBuiltin(t, Ucent) == Ucent);
236 		assert(promoteBuiltin(Ucent, t) == Ucent);
237 	}
238 }}
239