-
Notifications
You must be signed in to change notification settings - Fork 59
Add open enums and enum methods with tests #631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
9165555
1d2cc22
56864a0
9855b59
f77dd61
9daafee
70acb9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -412,7 +412,9 @@ class IrModule { | |
| return ic; | ||
| } | ||
| def newIrClass(ctype: Type, superClass: IrClass, decl: VstCompound) -> IrClass { | ||
| var ic = IrBuilder.new(ctype, superClass).buildClass(decl); | ||
| var builder = IrBuilder.new(ctype, superClass); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know why it feels the need to add the IR module to the builder now.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently the field and this code are dead, so I'm removing it. |
||
| builder.irModule = this; | ||
| var ic = builder.buildClass(decl); | ||
| classes.put(ic); | ||
| return ic; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -158,6 +158,10 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) | |
| if (rc.isUnboxed()) { | ||
| // move flattened data type receiver to function sig | ||
| ftype = Function.prependParamTypes(rc.variantNorm.sub, ftype); | ||
| } else if (EnumType.?(rc.oldType)) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If enums are properly desugared to variants, this will just fall out of the normal unboxing.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| // enum: prepend tag type so dispatch table CallFunction matches CallMethod | ||
| var tagType = V3.getVariantTagType(rc.oldType); | ||
| ftype = Function.prependParamType(tagType, ftype); | ||
| } | ||
| rm.funcNorm = FuncNorm.!(norm(ftype)); | ||
| var typeParams = if(rm.spec != null, rm.spec.getTypes().methodTypeArgs); | ||
|
|
@@ -588,11 +592,15 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) | |
| rm.norm.flags |= IrFlag.M_OVERRIDE; | ||
| sm.norm.flags |= IrFlag.M_OVERRIDDEN; | ||
| } | ||
| // For enum methods, M_OVERRIDDEN must be transferred from original | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know why it thinks this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ultimately this appears necessary because enums don't use regular vtables so their overrides are processed differently. That don't use regular vtables because individual enum cases are not types and thus don't have a corresponding |
||
| // because enum case overrides don't share vtable slots. | ||
| if (m.flags.M_OVERRIDDEN) rm.norm.flags |= IrFlag.M_OVERRIDDEN; | ||
| if (rm.virtual != null) virtuals = List.new(rm.virtual, virtuals); | ||
| } | ||
| def layoutMtable(rv: RaVirtual) { | ||
| if (rv.mtable != null) return; | ||
| var rm = rv.raMethod, rc = ra.getClass(rm.receiver); | ||
| if (EnumType.?(rc.oldType)) return layoutEnumMtable(rv, rm, rc); | ||
| var size = rc.maxClassId - rc.minClassId; | ||
| if (ra.compiler.RaDevirtualize && size == 1) return; // no need for an mtable | ||
| var table = Array<IrMethod>.new(size), mtable = IrMtable.new(rm.norm, rc.minClassId, table); | ||
|
|
@@ -630,6 +638,37 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) | |
| setMtable(l.head, rv); | ||
| } | ||
| } | ||
| def layoutEnumMtable(rv: RaVirtual, rm: RaMethod, rc: RaClass) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More replications with special cases from our AI friends. As this is very tricky, we definitely don't want to do this again.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Part of the same effects from enum cases not having an |
||
| var enumDecl = EnumType.!(rc.oldType).enumDecl; | ||
| var numCases = enumDecl.enumTagHi + 1; | ||
| var table = Array<IrMethod>.new(numCases); | ||
| var mtable = IrMtable.new(rm.norm, 0, table); | ||
| rv.mtable = mtable; | ||
|
|
||
| var ft = Function.funcRefType(rm.norm.getMethodType()); | ||
| mtable.record = ra.prog.newRecord(V3Array.newType(ft), numCases); | ||
|
|
||
| // Fill all slots with the default implementation. | ||
| var defaultSpec = IrSpec.new(rm.norm.receiver, [rm.norm.receiver], rm.norm); | ||
| for (i < numCases) { | ||
| table[i] = rm.norm; | ||
| mtable.record.values[i] = FuncVal.new(defaultSpec); | ||
| } | ||
|
|
||
| // Fill override slots. | ||
| var rootVst = rm.orig.source; | ||
| if (rootVst != null && rootVst.enumCaseIrs != null) { | ||
| for (i < rootVst.enumCaseIrs.length) { | ||
| var overrideIr = rootVst.enumCaseIrs[i]; | ||
| if (overrideIr == null) continue; | ||
| var overrideRm = overrideIr.raMethod; | ||
| if (overrideRm == null || !overrideRm.raFacts.RM_LIVE) continue; | ||
| table[i] = overrideRm.norm; | ||
| var ta = Arrays.replace(overrideRm.getSpec().typeArgs, 0, overrideRm.norm.receiver); | ||
| mtable.record.values[i] = FuncVal.new(IrSpec.new(ta[0], ta, overrideRm.norm)); | ||
| } | ||
| } | ||
| } | ||
| def resolveMethodImpl(rc: RaClass, rm: RaMethod) -> RaMethod { | ||
| var sm: RaMethod; | ||
| for (sc = rc; sc != null; sc = sc.parent) { // find super method, if any | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From this it's clear to me that enums need to be desugared to variants earlier in a more general way. Claude has just smeared special cases from front to middle to backend. There shouldn't be any need to alter the evaluation of operators like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm willing to work on simplifying such things. I'm not 100% clear on the desugaring you're hoping for, though. I can see that enums are kind of like variants that have no variant fields. Of course enums have their own "fields", that are implemented as arrays indexed by tag. If the two are to be unified into a single whole, we need some place to hang both kinds of fields. Also while enum class structure mirrors variant class structure (in the case of no generic parameters), enums themselves are single values (or have unique single values associated with them) while variant cases are types (unless they have no fields, in which case they have the same "ambiguity" as enums). If you have a little more guidance I would be more confident revising code ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, forgive me. It used to be that enums were immediately desugared to a single class declaration. At some point I split off the implementation entirely. If open enums were desugared into open variants instead (i.e. adding a class per enum case), then enum method dispatch would just be variant dispatch. Enum fields would still be implemented via field arrays and use
VariantGetTagas the index. Unboxing will just eliminate the overhead of boxing enum values.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get the idea but am foggy as to the particulars. Specifically I wonder about the field arrays, and maybe also enumsets. Is the desugaring proposed to happen in
VstSsaGen, so that enums are mostly washed away once we have SSA, but the distinction is more visible in parsing (of course) and verification? This would make sense in that, once verified, enums could be mapped down to other structures (variants, field arrays) and marked to be unboxed (always). I'd have to get more deeply into it to see where issues might come up with enumsets, but again, suitable use ofVariantGetTagwould get the raw numeric values required. So, I could try to combine processing in verification (and maybe parts of parsing), but strive to eliminate separate processing once in SSA.