diff --git a/codon/parser/visitors/typecheck/call.cpp b/codon/parser/visitors/typecheck/call.cpp index ed43095f..0da5158e 100644 --- a/codon/parser/visitors/typecheck/call.cpp +++ b/codon/parser/visitors/typecheck/call.cpp @@ -844,6 +844,25 @@ ExprPtr TypecheckVisitor::transformHasAttr(CallExpr *expr) { } } + if (typ->getUnion()) { + ExprPtr cond = nullptr; + auto unionTypes = typ->getUnion()->getRealizationTypes(); + int tag = -1; + for (size_t ui = 0; ui < unionTypes.size(); ui++) { + auto tu = realize(unionTypes[ui]); + if (!tu) + return nullptr; + auto te = N(tu->getClass()->realizedTypeName()); + auto e = N( + N(N("isinstance"), expr->args[0].value, te), "&&", + N(N("hasattr"), te, N(member))); + cond = !cond ? e : N(cond, "||", e); + } + if (!cond) + return transform(N(false)); + return transform(cond); + } + bool exists = !ctx->findMethod(typ->getClass().get(), member).empty() || ctx->findMember(typ->getClass(), member); if (exists && args.size() > 1) diff --git a/test/parser/types.codon b/test/parser/types.codon index 3852deb1..1efb0393 100644 --- a/test/parser/types.codon +++ b/test/parser/types.codon @@ -2030,3 +2030,30 @@ def correlate(a, b, mode = 'valid'): raise ValueError(f"mode must be one of 'valid', 'same', or 'full' (got {repr(mode)})") return xret print(correlate([1], [2], 'full')) # 5z + +#%% union_hasattr,barebones +class A: + def foo(self): + print('foo') + def bar(self): + print('bar') +class B: + def foo(self): + print('foo') + def baz(self): + print('baz') + +a = A() +print(hasattr(a, 'foo'), hasattr(a, 'bar'), hasattr(a, 'baz')) +#: True True False +b = B() +print(hasattr(b, 'foo'), hasattr(b, 'bar'), hasattr(b, 'baz')) +#: True False True + +c: Union[A, B] = A() +print(hasattr(c, 'foo'), hasattr(c, 'bar'), hasattr(c, 'baz')) +#: True True False + +c = B() +print(hasattr(c, 'foo'), hasattr(c, 'bar'), hasattr(c, 'baz')) +#: True False True