Deal with null monos.
This commit is contained in:
@@ -1,275 +1,275 @@
|
|||||||
package tink.macro;
|
package tink.macro;
|
||||||
|
|
||||||
import haxe.macro.Printer;
|
import haxe.macro.Printer;
|
||||||
|
|
||||||
import haxe.macro.Context;
|
import haxe.macro.Context;
|
||||||
import haxe.macro.Expr;
|
import haxe.macro.Expr;
|
||||||
import haxe.macro.Type;
|
import haxe.macro.Type;
|
||||||
|
|
||||||
using haxe.macro.Tools;
|
using haxe.macro.Tools;
|
||||||
using tink.MacroApi;
|
using tink.MacroApi;
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
class Types {
|
class Types {
|
||||||
|
|
||||||
static public function definedType(typeName:String)
|
static public function definedType(typeName:String)
|
||||||
return
|
return
|
||||||
try {
|
try {
|
||||||
Some(Context.getType(typeName));
|
Some(Context.getType(typeName));
|
||||||
}
|
}
|
||||||
catch (e:Dynamic)
|
catch (e:Dynamic)
|
||||||
if (Std.string(e) == 'Type not found \'$typeName\'') None;
|
if (Std.string(e) == 'Type not found \'$typeName\'') None;
|
||||||
else tink.core.Error.rethrow(e);
|
else tink.core.Error.rethrow(e);
|
||||||
|
|
||||||
static var types = new Map<Int,Void->Type>();
|
static var types = new Map<Int,Void->Type>();
|
||||||
static var idCounter = 0;
|
static var idCounter = 0;
|
||||||
static public function getID(t:Type, ?reduced = true)
|
static public function getID(t:Type, ?reduced = true)
|
||||||
return
|
return
|
||||||
if (reduced)
|
if (reduced)
|
||||||
getID(reduce(t), false);
|
getID(reduce(t), false);
|
||||||
else
|
else
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case TAbstract(t, _): t.toString();
|
case TAbstract(t, _): t.toString();
|
||||||
case TInst(t, _): t.toString();
|
case TInst(t, _): t.toString();
|
||||||
case TEnum(t, _): t.toString();
|
case TEnum(t, _): t.toString();
|
||||||
case TType(t, _): t.toString();
|
case TType(t, _): t.toString();
|
||||||
default: null;
|
default: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function accessToName(v:VarAccess, ?read = true)
|
static public function accessToName(v:VarAccess, ?read = true)
|
||||||
return
|
return
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case AccNormal, AccInline: 'default';
|
case AccNormal, AccInline: 'default';
|
||||||
case AccNo: 'null';
|
case AccNo: 'null';
|
||||||
case AccNever: 'never';
|
case AccNever: 'never';
|
||||||
case AccCall: if (read) 'get' else 'set';
|
case AccCall: if (read) 'get' else 'set';
|
||||||
default:
|
default:
|
||||||
throw 'not implemented';
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function getMeta(type:Type)
|
static public function getMeta(type:Type)
|
||||||
return switch type {
|
return switch type {
|
||||||
case TInst(_.get().meta => m, _): [m];
|
case TInst(_.get().meta => m, _): [m];
|
||||||
case TEnum(_.get().meta => m, _): [m];
|
case TEnum(_.get().meta => m, _): [m];
|
||||||
case TAbstract(_.get().meta => m, _): [m];
|
case TAbstract(_.get().meta => m, _): [m];
|
||||||
case TType(_.get() => t, _): [t.meta].concat(getMeta(t.type));
|
case TType(_.get() => t, _): [t.meta].concat(getMeta(t.type));
|
||||||
case TLazy(f): getMeta(f());
|
case TLazy(f): getMeta(f());
|
||||||
default: [];
|
default: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getDeclaredFields(t:ClassType, out:Array<ClassField>, marker:Map<String,Bool>) {
|
static function getDeclaredFields(t:ClassType, out:Array<ClassField>, marker:Map<String,Bool>) {
|
||||||
for (field in t.fields.get())
|
for (field in t.fields.get())
|
||||||
if (!marker.exists(field.name)) {
|
if (!marker.exists(field.name)) {
|
||||||
marker.set(field.name, true);
|
marker.set(field.name, true);
|
||||||
out.push(field);
|
out.push(field);
|
||||||
}
|
}
|
||||||
if (t.isInterface)
|
if (t.isInterface)
|
||||||
for (t in t.interfaces)
|
for (t in t.interfaces)
|
||||||
getDeclaredFields(t.t.get(), out, marker);
|
getDeclaredFields(t.t.get(), out, marker);
|
||||||
else if (t.superClass != null)
|
else if (t.superClass != null)
|
||||||
getDeclaredFields(t.superClass.t.get(), out, marker);
|
getDeclaredFields(t.superClass.t.get(), out, marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
static var fieldsCache = new Map();
|
static var fieldsCache = new Map();
|
||||||
static public function getFields(t:Type, ?substituteParams = true)
|
static public function getFields(t:Type, ?substituteParams = true)
|
||||||
return
|
return
|
||||||
switch (reduce(t)) {
|
switch (reduce(t)) {
|
||||||
case TInst(c, params):
|
case TInst(c, params):
|
||||||
var id = c.toString(),
|
var id = c.toString(),
|
||||||
c = c.get();
|
c = c.get();
|
||||||
if (!fieldsCache.exists(id)) {
|
if (!fieldsCache.exists(id)) {
|
||||||
var fields = [];
|
var fields = [];
|
||||||
getDeclaredFields(c, fields, new Map());
|
getDeclaredFields(c, fields, new Map());
|
||||||
fieldsCache.set(id, Success(fields));
|
fieldsCache.set(id, Success(fields));
|
||||||
}
|
}
|
||||||
var ret = fieldsCache.get(id);
|
var ret = fieldsCache.get(id);
|
||||||
if (substituteParams && ret.isSuccess()) {
|
if (substituteParams && ret.isSuccess()) {
|
||||||
var fields = Reflect.copy(ret.sure());
|
var fields = Reflect.copy(ret.sure());
|
||||||
|
|
||||||
for (field in fields)
|
for (field in fields)
|
||||||
field.type = haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params);
|
field.type = haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params);
|
||||||
}
|
}
|
||||||
fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache
|
fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache
|
||||||
ret;
|
ret;
|
||||||
case TAnonymous(anon): Success(anon.get().fields);
|
case TAnonymous(anon): Success(anon.get().fields);
|
||||||
default: Context.currentPos().makeFailure('type $t has no fields');
|
default: Context.currentPos().makeFailure('type $t has no fields');
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function getStatics(t:Type)
|
static public function getStatics(t:Type)
|
||||||
return
|
return
|
||||||
switch (reduce(t)) {
|
switch (reduce(t)) {
|
||||||
case TInst(t, _): Success(t.get().statics.get());
|
case TInst(t, _): Success(t.get().statics.get());
|
||||||
default: Failure('type has no statics');
|
default: Failure('type has no statics');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static public function getPosition(t:Type)
|
static public function getPosition(t:Type)
|
||||||
return
|
return
|
||||||
switch t {
|
switch t {
|
||||||
case TInst(_.get() => {pos: pos}, _)
|
case TInst(_.get() => {pos: pos}, _)
|
||||||
| TAbstract(_.get() => {pos: pos}, _)
|
| TAbstract(_.get() => {pos: pos}, _)
|
||||||
| TType(_.get() => {pos: pos}, _)
|
| TType(_.get() => {pos: pos}, _)
|
||||||
| TEnum(_.get() => {pos: pos}, _) : Success(pos);
|
| TEnum(_.get() => {pos: pos}, _) : Success(pos);
|
||||||
case TMono(ref): getPosition(ref.get());
|
case TMono(_.get() => t) if (t != null): getPosition(t);
|
||||||
case TLazy(f): getPosition(f());
|
case TLazy(f): getPosition(f());
|
||||||
case TDynamic(v) if(v != null): getPosition(v);
|
case TDynamic(v) if(v != null): getPosition(v);
|
||||||
default: Failure('type "$t" has no position');
|
default: Failure('type "$t" has no position');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static public function toString(t:ComplexType)
|
static public function toString(t:ComplexType)
|
||||||
return new Printer().printComplexType(t);
|
return new Printer().printComplexType(t);
|
||||||
|
|
||||||
static public function isSubTypeOf(t:Type, of:Type, ?pos)
|
static public function isSubTypeOf(t:Type, of:Type, ?pos)
|
||||||
return
|
return
|
||||||
ECheckType(ECheckType(macro null, toComplex(t)).at(pos), toComplex(of)).at(pos).typeof();
|
ECheckType(ECheckType(macro null, toComplex(t)).at(pos), toComplex(of)).at(pos).typeof();
|
||||||
|
|
||||||
static public function isDynamic(t:Type)
|
static public function isDynamic(t:Type)
|
||||||
return switch reduce(t) {
|
return switch reduce(t) {
|
||||||
case TDynamic(_): true;
|
case TDynamic(_): true;
|
||||||
default: false;
|
default: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function toType(t:ComplexType, ?pos:Position)
|
static public function toType(t:ComplexType, ?pos:Position)
|
||||||
return (macro @:pos(pos.sanitize()) {
|
return (macro @:pos(pos.sanitize()) {
|
||||||
var v:$t = null;
|
var v:$t = null;
|
||||||
v;
|
v;
|
||||||
}).typeof();
|
}).typeof();
|
||||||
|
|
||||||
static public inline function instantiate(t:TypePath, ?args, ?pos)
|
static public inline function instantiate(t:TypePath, ?args, ?pos)
|
||||||
return ENew(t, args == null ? [] : args).at(pos);
|
return ENew(t, args == null ? [] : args).at(pos);
|
||||||
|
|
||||||
static public function asTypePath(s:String, ?params):TypePath {
|
static public function asTypePath(s:String, ?params):TypePath {
|
||||||
var parts = s.split('.');
|
var parts = s.split('.');
|
||||||
var name = parts.pop(),
|
var name = parts.pop(),
|
||||||
sub = null;
|
sub = null;
|
||||||
if (parts.length > 0 && parts[parts.length - 1].charCodeAt(0) < 0x5B) {
|
if (parts.length > 0 && parts[parts.length - 1].charCodeAt(0) < 0x5B) {
|
||||||
sub = name;
|
sub = name;
|
||||||
name = parts.pop();
|
name = parts.pop();
|
||||||
if(sub == name) sub = null;
|
if(sub == name) sub = null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
pack: parts,
|
pack: parts,
|
||||||
params: params == null ? [] : params,
|
params: params == null ? [] : params,
|
||||||
sub: sub
|
sub: sub
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static public inline function asComplexType(s:String, ?params)
|
static public inline function asComplexType(s:String, ?params)
|
||||||
return TPath(asTypePath(s, params));
|
return TPath(asTypePath(s, params));
|
||||||
|
|
||||||
static public function reduce(type:Type, ?once) {
|
static public function reduce(type:Type, ?once) {
|
||||||
function rec(t:Type)
|
function rec(t:Type)
|
||||||
return if (once) t else reduce(t, false);
|
return if (once) t else reduce(t, false);
|
||||||
return switch type {
|
return switch type {
|
||||||
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t);
|
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t);
|
||||||
case TLazy(f): rec(f());
|
case TLazy(f): rec(f());
|
||||||
case TType(_, _): rec(Context.follow(type, once));
|
case TType(_, _): rec(Context.follow(type, once));
|
||||||
default: type;
|
default: type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function isVar(field:ClassField)
|
static public function isVar(field:ClassField)
|
||||||
return switch (field.kind) {
|
return switch (field.kind) {
|
||||||
case FVar(_, _): true;
|
case FVar(_, _): true;
|
||||||
default: false;
|
default: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function register(type:Void->Type):Int {
|
static public function register(type:Void->Type):Int {
|
||||||
types.set(idCounter, type);
|
types.set(idCounter, type);
|
||||||
return idCounter++;
|
return idCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function paramsToComplex(params:Array<Type>):Array<TypeParam>
|
static function paramsToComplex(params:Array<Type>):Array<TypeParam>
|
||||||
return [for (p in params) TPType(toComplex(p))];
|
return [for (p in params) TPType(toComplex(p))];
|
||||||
|
|
||||||
static function baseToComplex(t:BaseType, params:Array<Type>)
|
static function baseToComplex(t:BaseType, params:Array<Type>)
|
||||||
return asComplexType(t.module + '.' + t.name, paramsToComplex(params));
|
return asComplexType(t.module + '.' + t.name, paramsToComplex(params));
|
||||||
|
|
||||||
static public function toComplex(type:Type, ?options:{ ?direct: Bool }):ComplexType {
|
static public function toComplex(type:Type, ?options:{ ?direct: Bool }):ComplexType {
|
||||||
var ret =
|
var ret =
|
||||||
if (options == null || options.direct != true) tink.macro.Sisyphus.toComplexType(type);
|
if (options == null || options.direct != true) tink.macro.Sisyphus.toComplexType(type);
|
||||||
else null;
|
else null;
|
||||||
if (ret == null)
|
if (ret == null)
|
||||||
ret = lazyComplex(function () return type);
|
ret = lazyComplex(function () return type);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function intersect(types:Array<ComplexType>, ?pos:Position):Outcome<ComplexType, Error> {
|
static public function intersect(types:Array<ComplexType>, ?pos:Position):Outcome<ComplexType, Error> {
|
||||||
|
|
||||||
if (types.length == 1) return Success(types[1]);
|
if (types.length == 1) return Success(types[1]);
|
||||||
|
|
||||||
var paths = [],
|
var paths = [],
|
||||||
fields = [];
|
fields = [];
|
||||||
|
|
||||||
for (t in types)
|
for (t in types)
|
||||||
switch t {
|
switch t {
|
||||||
case TPath(p): paths.push(p);
|
case TPath(p): paths.push(p);
|
||||||
case TAnonymous(f):
|
case TAnonymous(f):
|
||||||
|
|
||||||
for (f in f) fields.push(f);
|
for (f in f) fields.push(f);
|
||||||
|
|
||||||
case TExtend(p, f):
|
case TExtend(p, f):
|
||||||
|
|
||||||
for (f in f) fields.push(f);
|
for (f in f) fields.push(f);
|
||||||
for (p in p) paths.push(p);
|
for (p in p) paths.push(p);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
return Failure(new Error(t.toString() + ' cannot be interesected', pos));
|
return Failure(new Error(t.toString() + ' cannot be interesected', pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(TExtend(paths, fields));
|
return Success(TExtend(paths, fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function lazyComplex(f:Void->Type)
|
static public function lazyComplex(f:Void->Type)
|
||||||
return
|
return
|
||||||
TPath({
|
TPath({
|
||||||
pack : ['tink','macro'],
|
pack : ['tink','macro'],
|
||||||
name : 'DirectType',
|
name : 'DirectType',
|
||||||
params : [TPExpr(register(f).toExpr())],
|
params : [TPExpr(register(f).toExpr())],
|
||||||
sub : null,
|
sub : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
static function resolveDirectType()
|
static function resolveDirectType()
|
||||||
return
|
return
|
||||||
switch reduce(Context.getLocalType()) {
|
switch reduce(Context.getLocalType()) {
|
||||||
case TInst(_, [TInst(_.get() => { kind: KExpr(e) }, _)]):
|
case TInst(_, [TInst(_.get() => { kind: KExpr(e) }, _)]):
|
||||||
types[e.getInt().sure()]();//When using compiler server, this call throws on occasion, in which case modifying this file (to update mtime and invalidate the cache) will solve the problem
|
types[e.getInt().sure()]();//When using compiler server, this call throws on occasion, in which case modifying this file (to update mtime and invalidate the cache) will solve the problem
|
||||||
default:
|
default:
|
||||||
throw 'assert';
|
throw 'assert';
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
|
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
|
||||||
if (follow) {
|
if (follow) {
|
||||||
t1 = t1.reduce();
|
t1 = t1.reduce();
|
||||||
t2 = t2.reduce();
|
t2 = t2.reduce();
|
||||||
}
|
}
|
||||||
|
|
||||||
return switch t1.getIndex() - t2.getIndex() {
|
return switch t1.getIndex() - t2.getIndex() {
|
||||||
case 0:
|
case 0:
|
||||||
Reflect.compare(t1.toString(), t2.toString());//much to my surprise, this actually seems to work (at least with 3.4)
|
Reflect.compare(t1.toString(), t2.toString());//much to my surprise, this actually seems to work (at least with 3.4)
|
||||||
case v: v;
|
case v: v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;
|
static var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;
|
||||||
|
|
||||||
static public function getFieldSuggestions(type:ComplexType, name:String):String
|
static public function getFieldSuggestions(type:ComplexType, name:String):String
|
||||||
return switch (macro (null : $type).$name).typeof() {
|
return switch (macro (null : $type).$name).typeof() {
|
||||||
case Failure(SUGGESTIONS.match(_.message) => true): SUGGESTIONS.matched(0);
|
case Failure(SUGGESTIONS.match(_.message) => true): SUGGESTIONS.matched(0);
|
||||||
default: '';
|
default: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function toDecl(p:TypeParameter):TypeParamDecl
|
static public function toDecl(p:TypeParameter):TypeParamDecl
|
||||||
return {
|
return {
|
||||||
name: p.name,
|
name: p.name,
|
||||||
constraints: switch p.t {
|
constraints: switch p.t {
|
||||||
case TInst(_.get() => { kind: KTypeParameter(c)}, _): [for(c in c) c.toComplex()];
|
case TInst(_.get() => { kind: KTypeParameter(c)}, _): [for(c in c) c.toComplex()];
|
||||||
case _: throw 'unreachable';
|
case _: throw 'unreachable';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user