diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index d26dc8d0f63..44eafa74b56 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -12257,6 +12257,49 @@ and TcLetBinding (cenv: cenv) isUse env containerInfo declKind tpenv (synBinds, let tpenv = HideUnscopedTypars generalizedTypars tpenv let valSchemes = NameMap.map (UseCombinedValReprInfo g declKind rhsExpr) prelimValSchemes2 let values = MakeAndPublishVals cenv env (altActualParent, false, declKind, ValNotInRecScope, valSchemes, attrs, xmlDoc, literalValue) + + // [realsig+] Register every top-level (non-rec) let inside a class-body so re-hosting + // (GetValReprTypeInfoInCompiledForm / GenMethodForBinding / GetIlxClosureFreeVars) + // knows the host-type typars it inherits. The rec-binding site (TcLetrecGeneralizeBinding + // at ~line 13344) only fires for `let rec`; a lone `let` in a member body (e.g. `memoize` + // at tests/.../GenericClassConstrainedClosure.fs:6) goes through TcLetBinding and was + // previously left unregistered — RealsigClosureParentTryGet(closureVal.Stamp) then + // returned None and the whole re-hosting fix silently did not apply. + if g.realsig then + match env.eFamilyType with + | Some hostTycon -> + let hostTypars = hostTycon.Typars + if not (List.isEmpty hostTypars) then + NameMap.iter (fun pair -> + let (v: Val) = fst pair + g.AddRealsigClosureParent(v, hostTypars, Some hostTycon)) values + | None -> + // [realsig+] Module-scope (no family type) non-rec closure: register any + // typars free in the closure's full type but not declared by its own + // signature (e.g. a surrounding function's inference typar the body + // captures via a bound local). These are the surrounding function's + // declared typars that the closure must NOT re-require as its own generic + // parameters. Mirrors the rec-binding site (TcLetrecGeneralizeBinding), + // which only fires for `let rec`; a lone `let` goes through here. Without + // this, RealsigClosureParentTryGet returns None, the TLR reqdTypars + // owner-typar filter (InnerLambdasToTopLevelFuncs packEnv) is skipped, and + // the re-hosted closure re-declares the typar it already captures, yielding + // a generic-parameter index out of bounds at its instantiation site. + // Guard on `values` being non-empty: a `let _ = ...` (wildcard) binding + // publishes no vals, so there is nothing to register and we skip. + if not (NameMap.isEmpty values) then + let declaredTypars0 = allDeclaredTypars + let (v0: Val) = NameMap.toList values |> List.head |> snd |> fst + let allFreeTyvars = (freeInType CollectAllNoCaching (v0.TauType)).FreeTypars |> Zset.elements + let familyTypars = + allFreeTyvars + |> List.filter (fun (t: Typar) -> + not (List.exists (fun (d: Typar) -> d.Name = t.Name && d.Range = t.Range) declaredTypars0)) + |> List.distinctBy (fun (t: Typar) -> (t.Name, t.Range)) + if not (List.isEmpty familyTypars) then + NameMap.iter (fun pair -> + let (v: Val) = fst pair + g.AddRealsigClosureParent(v, familyTypars, None)) values let checkedPat = tcPatPhase2 (TcPatPhase2Input (values, true)) let prelimRecValues = let prelimRecValues = NameMap.map fst values @@ -13306,12 +13349,42 @@ and TcIncrementalLetRecGeneralization cenv scopem let generalizedTyparsL = newGeneralizableBindings |> List.map (TcLetrecComputeAndGeneralizeGenericTyparsForBinding cenv denv freeInEnv) + // realsig+ fix: register each closure's owner typars so TLR (InnerLambdasToTopLevelFuncs + // FlatEnvPacks.packEnv + createFHat) can subtract them from reqdTypars when re-hosting + // the closure as a nested method of a class. + // + // ONLY register when there is a genuine class owner (envNonRec.eFamilyType.IsSome). The + // re-hosted closure then lives as a nested method of that class, so the class supplies + // those typars via its own scope and the closure must not re-declare them. + // + // Do NOT register for module-scope closures (envNonRec.eFamilyType = None, e.g. a + // closure inside a generic top-level function such as binaryChop<'T> capturing 'T). The + // re-hosted version (go@N / loop@N) is a STANDALONE generic method whose OWN signature + // declares the captured typar at index 0..k-1; treating it as an owner typar reserves a + // leading ambient slot in the body's type-env and shifts every own typar index up by + // |ownTypars|, so the body emits !!1 / !!2.. where the method only declared !!0 / !!1, + // an out-of-range token -> PEVerify fails. There is no class to supply the typar at the + // call site, so it must remain the method's own declared typar. + if g.realsig then + match envNonRec.eFamilyType with + | Some hostTycon -> + let familyTypars = hostTycon.Typars + if not (List.isEmpty familyTypars) then + List.iter (fun (pgrbind: PreGeneralizationRecursiveBinding) -> + let rbinfo = pgrbind.RecBindingInfo + if rbinfo.DeclKind = DeclKind.ExpressionBinding then + g.AddRealsigClosureParent(rbinfo.Val, familyTypars, Some hostTycon)) newGeneralizableBindings + | None -> + // Module scope: no class owner. The captured typar, if any, is the closure's + // own generalized typar (declared on the re-hosted method's signature); leave + // it unregistered so the standalone method keeps it as its own declared typar. + () + // Generalize the bindings. let newGeneralizedRecBinds = (generalizedTyparsL, newGeneralizableBindings) ||> List.map2 (TcLetrecGeneralizeBinding cenv denv ) let tpenv = HideUnscopedTypars (List.concat generalizedTyparsL) tpenv newGeneralizedRecBinds, tpenv - newGeneralizedRecBinds, preGeneralizationRecBinds, tpenv let envNonRec = envNonRec |> AddLocalVals g cenv.tcSink scopem (newGeneralizedRecBinds |> List.map (fun b -> b.RecBindingInfo.Val)) @@ -13353,6 +13426,7 @@ and TcLetrecComputeAndGeneralizeGenericTyparsForBinding cenv denv freeInEnv (pgr let canGeneralizeConstrained = GeneralizationHelpers.CanGeneralizeConstrainedTyparsForDecl rbinfo.DeclKind let generalizedTypars = GeneralizationHelpers.ComputeAndGeneralizeGenericTypars (cenv, denv, m, freeInEnv, canInferTypars, canGeneralizeConstrained, inlineFlag, Some expr, allDeclaredTypars, maxInferredTypars, tau, isCtor) + generalizedTypars /// Compute the type variables which may have member constraints that need to be canonicalized prior to generalization diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 2b28167974b..c3020f245e3 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -556,6 +556,18 @@ type TypeReprEnv /// Check if a type parameter is in the environment member _.ContainsKey(tp: Typar) = reprs.ContainsKey(tp.Stamp) + /// Lookup a type parameter by Name+Range (survives cloning). Used in realsig+ to resolve + /// body typar clones to the host class's canonical typars when stamp lookup fails. + /// Returns the IL type variable index if a match is found. + member _.TryFindIndexNameRange(tp: Typar) : uint16 option = + reprs + |> Map.toList + |> List.tryPick (fun (_, (idx, t)) -> + if tp.Name = t.Name && tp.Range = t.Range then + Some idx + else + None) + /// Add an additional type parameter to the environment. If the parameter is a units-of-measure parameter /// then it is ignored, since it doesn't correspond to a .NET type parameter. member tyenv.AddOne(tp: Typar) = @@ -737,8 +749,17 @@ and GenTypeAux cenv m (tyenv: TypeReprEnv) voidOK ptrsOK ty = EraseClosures.mkILTyFuncTy cenv.ilxPubCloEnv | TType_var(tp, _) -> + if tyenv.ContainsKey tp then mkILTyvarTy tyenv[tp, m] + elif g.realsig then + // realsig+: body typars may be clones of the host class typars (different stamp, same Name+Range). + // Resolve by Name+Range to the class's canonical typar so it emits as !0/!1 rather than a concrete default. + match tyenv.TryFindIndexNameRange tp with + | Some idx -> mkILTyvarTy idx + | None -> + let defaultTy = TypeRelations.ChooseTyparSolution g cenv.amap tp + GenTypeAux cenv m tyenv voidOK ptrsOK defaultTy else // Unsolved type variable not in the TypeReprEnv — can arise for inline SRTP // functions where constraint resolution leaves phantom typars unsolved. @@ -1492,16 +1513,47 @@ let slotSigRequiresNativePtrRewrite (g: TcGlobals) ty (sctps: Typars) (sParams: |> List.exists (fun (TSlotParam(_, paramTy, _, _, _, _)) -> hasNativePtrWithTypar g sctps paramTy) || sRetTy |> Option.exists (hasNativePtrWithTypar g sctps)) +let GetValReprTypeInfoInCompiledForm g valReprInfo (vref: ValRef) m = + let numEnclosingTypars = CountEnclosingTyparsOfActualParentOfVal vref.Deref + + let tps, witnessInfos, curriedArgInfos, returnTy, retInfo = + GetValReprTypeInCompiledForm g valReprInfo numEnclosingTypars vref.Type m + + // [realsig+] For a closure re-hosted as a nested static method of its host class, the + // method must declare the owner (host-class) typars as leading generic parameters + // (so that the body can reference them as !0..!k) followed by the method's own + // typars (indices k+1..n-1, emitted as !!...). The historical ownerTypars @ tps + // branch did not achieve this, because tps (the stripped list from + // GetValReprTypeInCompiledForm) already excludes the enclosing typars when + // numEnclosingTypars > 0, so the method's declared count was too small for the + // body's references — the resulting FSharpFunc (a vs b) mismatch is the + // "2 errors in memoize@6" symptom. The fix: rebuild the parameter list as + // [ownerTypars (from RealsigClosureParent)] + [method's own typars (from + // vref.Type = TyLambda([...], ...), which for a re-hosted TLR closure carries + // its own typars in TpsAll form)]. vref.Type is used instead of tps because + // tps has had the leading numEnclosingTypars entries already dropped. + let tpsAfter = + match g.RealsigClosureParentTryGet(vref.Deref) with + | Some info when g.realsig && vref.IsCompiledAsTopLevel && info.ownerTycon.IsSome -> + let own = + match vref.Type with + | TType_forall(tpsAll, _) -> tpsAll + | _ -> [] + + info.ownerTypars @ own + | _ -> tps + + tpsAfter, witnessInfos, curriedArgInfos, returnTy, retInfo + /// Determine how a top level value is represented, when it is being represented /// as a method. let GetMethodSpecForMemberVal cenv (memberInfo: ValMemberInfo) (vref: ValRef) = let g = cenv.g let m = vref.Range - let numEnclosingTypars = CountEnclosingTyparsOfActualParentOfVal vref.Deref let tps, witnessInfos, curriedArgInfos, returnTy, retInfo = assert vref.ValReprInfo.IsSome - GetValReprTypeInCompiledForm g vref.ValReprInfo.Value numEnclosingTypars vref.Type m + GetValReprTypeInfoInCompiledForm g vref.ValReprInfo.Value vref m let tyenvUnderTypars = TypeReprEnv.Empty.ForTypars tps let flatArgInfos = List.concat curriedArgInfos @@ -1688,16 +1740,23 @@ let ComputeStorageForFSharpMember cenv valReprInfo memberInfo (vref: ValRef) m = let ComputeStorageForFSharpFunctionOrFSharpExtensionMember (cenv: cenv) cloc valReprInfo (vref: ValRef) m = let g = cenv.g let nm = vref.CompiledName g.CompilerGlobalState - let numEnclosingTypars = CountEnclosingTyparsOfActualParentOfVal vref.Deref let tps, witnessInfos, curriedArgInfos, returnTy, retInfo = - GetValReprTypeInCompiledForm g valReprInfo numEnclosingTypars vref.Type m + GetValReprTypeInfoInCompiledForm g valReprInfo vref m let tyenvUnderTypars = TypeReprEnv.Empty.ForTypars tps let methodArgTys, paramInfos = curriedArgInfos |> List.concat |> List.unzip let ilMethodArgTys = GenParamTypes cenv m tyenvUnderTypars false methodArgTys let ilRetTy = GenReturnType cenv m tyenvUnderTypars returnTy - let ilLocTy = mkILTyForCompLoc cloc + + let ilLocTy = + match g.RealsigClosureParentTryGet(vref.Deref) with + | Some info when g.realsig && vref.IsCompiledAsTopLevel -> + match info.ownerTycon with + | Some tycon -> GenType cenv m TypeReprEnv.Empty (TType_app(tycon, List.map mkTyparTy tps, g.knownWithoutNull)) + | None -> mkILTyForCompLoc cloc + | _ -> mkILTyForCompLoc cloc + let ilMethodInst = GenTypeArgs cenv m tyenvUnderTypars (List.map mkTyparTy tps) let mspec = @@ -4785,16 +4844,79 @@ and GenApp (cenv: cenv) cgbuf eenv (f, fty, tyargs, curriedArgs, m) sequel = // numEnclILTypeArgs will include unit-of-measure args, unfortunately. For now, just cut-and-paste code from GetMemberCallInfo // @REVIEW: refactor this + // For re-hosted closures (TLR fHat) the call-site Val has MemberInfo=None and + // is NOT the Val that CheckExpressions registered in g.RealsigClosureParents + // (keyed by the original closure-binding Val's stamp; TLR never re-registers the + // re-hosted method's Val). Recover the host/owner typars from the caller's + // ambient type-parameter environment (eenv.tyenv), which contains the host + // class's typars (e.g. 'T, 'U of ConcatEnumerator<'T,'U>) because the call + // site is inside a member of that class. This works uniformly for all + // re-hosted closures, including those (like iter@272) whose *type* contains + // no free occurrences of the host typars (the typars are captured only as + // substituted values in the closure body). + let ambientTypars = + match vref.MemberInfo with + | Some _ when not vref.IsExtensionMember -> [] + | _ when cenv.g.realsig -> + (match cenv.g.RealsigClosureParentTryGet(vref.Deref) with + // For module-scope re-hosted closures (ownerTycon = None) the owning + // module class has 0 type parameters, so no type arguments are + // consumed by the class; all type arguments bind the method itself. + | Some info when info.ownerTycon.IsNone -> [] + | Some info -> info.ownerTypars + | None -> + // Re-hosted TLR closures are compiled as top-level methods that are + // not member/module bindings. Only those inherit the host class's + // typars; ordinary module functions (e.g. MyCollections.IEnumerator.check) + // must not, or a non-generic module gets spurious generic args. + // + // [realsig+] Additionally, the callee's DeclaringEntity must itself be + // a generic type (i.e. have >0 own typars). A call to a module-level + // value (e.g. Test.patternInput@24) from inside a re-hosted TLR + // closure's body (Test/f@29TT::Invoke) must NOT inherit the host + // class's ambient typars: the callee is not a re-hosted closure, it + // is a plain top-level value whose DeclaringEntity (the module class) + // has 0 typars. The bug at hand (Issue_0002) is precisely this: + // f@29TT::Invoke called Test::patternInput@24, and the code emitted + // Test::patternInput@24() where !a,!b are the + // ambient class typars, but Test is non-generic → TypeLoadException. + // Guard: only use the ambient typars when the callee's declaring + // entity is a generic type with >0 own typars. + if + (vref.IsCompiledAsTopLevel || vref.IsCompilerGenerated) + && not (vref.IsMember || vref.IsMemberOrModuleBinding) + && (match vref.TryDeclaringEntity with + | Parent dEnt -> List.length (dEnt.Typars |> DropErasedTypars) > 0 + | ParentNone -> false) + then + Zset.elements (eenv.tyenv.AsUserProvidedTypars()) + else + []) + + | _ -> [] + let numEnclILTypeArgs = match vref.MemberInfo with | Some _ when not vref.IsExtensionMember -> List.length (vref.MemberApparentEntity.Typars |> DropErasedTypars) + | _ when cenv.g.realsig -> + (match cenv.g.RealsigClosureParentTryGet(vref.Deref) with + // Module-scope re-hosted closures own no class type arguments. + | Some info when info.ownerTycon.IsNone -> 0 + | Some info -> List.length info.ownerTypars + | None -> List.length ambientTypars) | _ -> 0 let ilEnclArgTys, ilMethArgTys = - if ilTyArgs.Length < numEnclILTypeArgs then - error (InternalError("length mismatch", m)) + if List.length ambientTypars > 0 then + // The closure's type does not mention the host typars (e.g. iter@272), + // so they are not in ilTyArgs. Reconstruct the class-instantiating + // args from the caller's ambient typars instead. + (ambientTypars |> List.map (fun tp -> GenType cenv m eenv.tyenv (mkTyparTy tp)), ilTyArgs) + else + (if ilTyArgs.Length < numEnclILTypeArgs then + error (InternalError("length mismatch", m)) - List.splitAt numEnclILTypeArgs ilTyArgs + List.splitAt numEnclILTypeArgs ilTyArgs) let boxity = mspec.DeclaringType.Boxity let valu = boxity = AsValue @@ -7714,10 +7836,12 @@ and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) boxity eenv takenNames let ilCloTypeRef = NestedTypeRefForCompLoc eenv.cloc cloName let initialFreeTyvars = + let userTypars = eenv.tyenv.AsUserProvidedTypars() + match g.realsig with | true -> { emptyFreeTyvars with - FreeTypars = eenv.tyenv.AsUserProvidedTypars() + FreeTypars = userTypars } | false -> emptyFreeTyvars @@ -10253,7 +10377,53 @@ and GenMethodForBinding // The type parameters of the method's type are different to the type parameters // for the big lambda ("tlambda") of the implementation of the method. - let eenvUnderMethLambdaTypars = EnvForTypars methLambdaTypars eenv + // [realsig+] For a closure re-hosted as a nested static method of its host class, + // the body's tyenv must include the host class's canonical typars (from eenv.tyenv, + // set by EnvForTycon in GenTypeDef) at IL indices 0..k-1, and the method's own + // typars at k..n-1. The default EnvForTypars resets to only the method's own typars, + // so the body's references to the class typars fall back to ChooseTyparSolution + // (concrete types) instead of the class's !0/!1. + let eenvUnderMethLambdaTypars = + if + cenv.g.realsig + && not (Option.isSome v.MemberInfo) + && not ( + v.IsExtensionMember + || (match g.RealsigClosureParentTryGet v with + | Some info when info.ownerTycon.IsNone -> true + | _ -> false) + ) + then + let ambientTypars = + match v.MemberInfo with + | Some _ when not v.IsExtensionMember -> [] + | _ when cenv.g.realsig -> + (match cenv.g.RealsigClosureParentTryGet(v) with + | Some info -> info.ownerTypars + | None -> + // [realsig+] A TLR'd top-level method (IsCompiledAsTopLevel / + // IsCompilerGenerated, NOT a member or module binding) carries its + // own typars in methLambdaTypars. Returning eenv.tyenv's + // user-provided typars as ambientTypars here would duplicate them + // (ambient=own, methLambda=own), shifting the body's own typars by + // |own| and making body (il-index |own|..2|own|-1) disagree with the + // signature (il-index 0..|own|-1) -> out-of-range ldelem/callvirt + // element types for loop@1[+T1,T2] / go@10412[+T]. An *unregistered* + // top-level re-hosted closure's own typars are the full set and + // must not be re-added as ambient. + []) + + | _ -> [] + + if List.isEmpty ambientTypars then + EnvForTypars methLambdaTypars eenv + else + { eenv with + tyenv = (eenv.tyenv.ResetTypars().Add ambientTypars).Add methLambdaTypars + } + else + EnvForTypars methLambdaTypars eenv + let eenvUnderMethTypeClassTypars = EnvForTypars ctps eenv let eenvUnderMethTypeTypars = AddTyparsToEnv mtps eenvUnderMethTypeClassTypars @@ -11061,7 +11231,11 @@ and AllocValReprWithinExpr cenv cgbuf endMark cloc v eenv = // storage without triggering the file's initialization when a lifted method is called. let effectiveCloc = if v.IsCompiledAsTopLevel && not v.IsMemberOrModuleBinding then - if eenv.moduleCloc.Enclosing.IsEmpty then + // With realsig=true these vals are hosted on the type that originated the closure + // With realsig=false these vals are hosted on the module enclosing the type that originated the closure + if cenv.g.realsig then + cloc + else if eenv.moduleCloc.Enclosing.IsEmpty then if IsFSharpValCompiledAsMethod cenv.g v then CompLocForTopLevelFunctions eenv.moduleCloc else diff --git a/src/Compiler/Optimize/DetupleArgs.fs b/src/Compiler/Optimize/DetupleArgs.fs index 72158b2d6b8..0df11dbe174 100644 --- a/src/Compiler/Optimize/DetupleArgs.fs +++ b/src/Compiler/Optimize/DetupleArgs.fs @@ -556,6 +556,13 @@ let mkTransform (scope: PerFileNamingScope) g (f: Val) m tps x1Ntys retTy (callP fCty valReprInfo + // [realsig+] Propagate the realsig closure parent from the original closure f + // to the transformed closure, since mkLocalVal mints a fresh stamp that is + // not in realsigClosureParents. Downstream TLR (RealsigClosureParentTryGet + // in InnerLambdasToTopLevelFuncs createFHat / FlatEnvPacks.packEnv) needs to + // find the record to subtract the owner typars from reqdTypars. + if g.realsig then g.RealsigClosureParentRekey(f, transformedVal) + { transformCallPattern = callPattern transformedFormals = transformedFormals transformedVal = transformedVal } diff --git a/src/Compiler/Optimize/InnerLambdasToTopLevelFuncs.fs b/src/Compiler/Optimize/InnerLambdasToTopLevelFuncs.fs index d990713b437..638a6c320ee 100644 --- a/src/Compiler/Optimize/InnerLambdasToTopLevelFuncs.fs +++ b/src/Compiler/Optimize/InnerLambdasToTopLevelFuncs.fs @@ -159,32 +159,6 @@ let IsRefusedTLR g (f: Val) = let refuseTest = alreadyChosen || mutableVal || byrefVal || specialVal || dllImportStubOrOtherNeverInline || isResumableCode || isInlineIfLambda refuseTest -/// Under --realsig+, a TLR-lifted helper is emitted at module scope (outside its declaring -/// type when that type is a class). If the helper's body invokes a source-`private` member -/// of a class/struct, the CLR raises MethodAccessException at runtime because IL `private` -/// is type-scoped. -/// -/// Private members of MODULES are not at risk: the lifted helper lands in the same module -/// IL class as the private val. F# RecdFields always compile to IL `assembly` or wider, so -/// field access is safe; only val/method references whose declaring entity is a class need -/// to be checked. -let BodyReferencesTypeScopedPrivate e = - let mutable found = false - let folder = - { ExprFolder0 with - exprIntercept = fun _recurseF noInterceptF z expr -> - if found then z - else - match expr with - | Expr.Val (vref, _, _) when vref.Accessibility.IsPrivate -> - match vref.TryDeclaringEntity with - | Parent eref when not eref.IsModuleOrNamespace -> found <- true - | _ -> () - | _ -> () - noInterceptF z expr } - FoldExpr folder () e |> ignore - found - let IsMandatoryTopLevel (f: Val) = let specialVal = f.MemberInfo.IsSome let isModulBinding = f.IsMemberOrModuleBinding @@ -214,18 +188,15 @@ module Pass1_DetermineTLRAndArities = elif Zset.contains f xinfo.DecisionTreeBindings then None - // Under --realsig+, lifting a helper out of its declaring type would lose access to - // any source-`private` members it references, producing MethodAccessException at runtime. - elif g.realsig && BodyReferencesTypeScopedPrivate e then - None - // #5302: a module-level static lifted out of its family would lose access to a protected base // field (FieldAccessException), so refuse the lift. + // realsig+ shouldn't have this problem because no lift is performed, so we don't refuse it. elif - g.langVersion.SupportsFeature LanguageFeature.AccessProtectedBaseFieldFromClosure + (not g.realsig) && g.langVersion.SupportsFeature LanguageFeature.AccessProtectedBaseFieldFromClosure && exprReferencesProtectedILField amap e then None + else // Could the binding be TLR? with what arity? let atTopLevel = Zset.contains f xinfo.TopLevelBindings @@ -795,7 +766,24 @@ let FlatEnvPacks g fclassM topValS declist (reqdItemsMap: Zmap List.exists (fun (v: Val) -> + match g.RealsigClosureParentTryGet v with + | Some info -> info.ownerTycon.IsSome + | None -> false) + let reqdTypars = + if g.realsig && isClassHosted && not (List.isEmpty ownerTypars) then + env.reqdTypars |> Zset.filter (fun (tp: Typar) -> not (List.exists (fun (o: Typar) -> tp.Name = o.Name && tp.Range = o.Range) ownerTypars)) + else + env.reqdTypars let aenvs = Zmap.values cmap let pack = cmapPairs |> List.map (fun (v, aenv) -> mkInvisibleBind aenv (exprForVal env.m v)) let unpack = @@ -834,6 +822,8 @@ let FlatEnvPacks g fclassM topValS declist (reqdItemsMap: Zmap id.idText+suffixForVariablesThatMayNotBeEliminated | None -> suffixForVariablesThatMayNotBeEliminated let fv, fe = mkCompGenLocal m nm (mkFunTy g g.unit_ty ty) + //TODO: review this for fragility + // [realsig+] Register the split-thunk in the realsigClosureParents table so the + // downstream TLR pass (FlatEnvPacks.packEnv and createFHat) subtracts the host type's + // typars from the re-hosted closure's reqdTypars. Existing remap code (Remapping.fs:1783-1795) + // propagates the record to any clone of fv that TLR selects, and CreateNewValuesForTLR + // propagates it to fHat. The split body closes over the generic type's declared typars + // (e.g. `'a` in `BigGenericTuple<'a>`) via its free variables — e.g. the `thisCast`/ + // `objCast` locals in `CompareTo` — so reconstruct the owner typars + TyconRef from there. + if g.realsig then + let freeInSplit = freeInExpr (CollectLocalsWithStackGuard()) e + let splitTypars = freeInSplit.FreeTyvars.FreeTypars |> Zset.elements + if not (List.isEmpty splitTypars) then + // [realsig+] Locate the host (owner) tycon the split body closes over. The + // free locals of the split body are the members of the host class (e.g. the + // thisCast/objCast fields of `BigGenericTuple<'a>`). Their types appear as + // either `TType_app(tcr,_)` (a direct generic application) or + // `TType_ucase(uc,_)` (a union-case field, whose `uc.TyconRef` is the host + // tycon). Pick the first free local whose type references a generic host tycon. + let splitTycon = + Zset.elements freeInSplit.FreeLocals + |> List.tryPick (fun (v: Val) -> + match v.Type with + | TType_app(tcr, _, _) when not (List.isEmpty tcr.Typars) -> Some tcr + | TType_ucase(uc, _) -> + let hostTcr = uc.TyconRef + if not (List.isEmpty hostTcr.Typars) then Some hostTcr + else None + | _ -> None) + g.AddRealsigClosureParent(fv, splitTypars, splitTycon) mkInvisibleLet m fv (mkLambda m uv (e, ty)) (primMkApp (fe, (mkFunTy g g.unit_ty ty)) [] [mkUnit g m] m), {einfo with FunctionSize=callSize } diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index b4b23ee2ff0..a75fba7a670 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -183,6 +183,25 @@ type CompilationMode = | Service | Interactive +/// Per-compilation, cross-phase bookkeeping used by --realsig+. +/// +/// When a closure (a binding of DeclKind.ExpressionBinding) is typechecked inside a +/// generic class, under --realsig+ it is re-hosted at the class (IlxGen.fs AllocValReprWithinExpr). +/// A nested method inherits the class's typars and cannot declare them. Under --realsig- the +/// same closure is lifted to the outer module, where declaring the typars is correct, so the +/// record is never made for that mode. +/// +/// `ownerOriginalTypars` are the class's typars at typecheck time (un-cloned). When the compiler +/// later clones typars (Remap.fs copyAndRemapAndBindTyparsFull) and a clone corresponds to one of +/// these original typars, the clone's stamp is appended to `clonedStamps`. TLR (accBinds in +/// InnerLambdasToTopLevelFuncs.fs) reads `clonedStamps` and subtracts them from reqdTypars so the +/// closure's ep_etps does not redeclare them. +[] +type RealsigClosureParentInfo = + { closureVal: Val + ownerTypars: Typars + ownerTycon: TyconRef option } + type TcGlobals( compilingFSharpCore: bool, ilg: ILGlobals, @@ -290,6 +309,19 @@ type TcGlobals( let mutable embeddedILTypeDefs = ConcurrentDictionary() + // [realsig+] Written from the typechecker (TcNormalizedBinding) and read from TLR (FlatEnvPacks). + // Keyed by closure Val.Stamp (int64) because `Val` is NoEquality. Only present when realsig is on. + let mutable realsigClosureParents : Map = Map.empty + + // RFC FS-1043: compilation-scoped record of how the type-checker resolved built-in-operator SRTP + // constraints to extension members. Keyed by (operator logical name, support-type encoding, argument-type + // encoding); each entry keeps the (source range, solution-identity, solution) of every site that resolved + // that concrete key. The optimizer consults this to honor the checker's scope-aware decision when an + // inlined FSharp.Core operator arrives without a trait context. When one concrete key is resolved to two + // different extensions in two different scopes of the same file, the recorded ranges disambiguate which + // site is being replayed (see TryGetExtensionOperatorSolution), so opening/shadowing distinct same-signature + // extensions stays sound instead of falling back to an ambiguous file-global re-resolution. + let dummyAssemblyNameCarryingUsefulErrorInformation path typeName = FSComp.SR.tcGlobalsSystemTypeNotFound (String.concat "." path + "." + typeName) @@ -1181,6 +1213,46 @@ type TcGlobals( member _.realsig = realsig + member _.AddRealsigClosureParent(closureVal: Val, ownerTypars: Typars, ownerTycon: TyconRef option) = + realsigClosureParents <- Map.add closureVal.Stamp { closureVal = closureVal; ownerTypars = ownerTypars; ownerTycon = ownerTycon } realsigClosureParents + + member _.RealsigClosureParentTryGet(closureVal: Val) = + Map.tryFind closureVal.Stamp realsigClosureParents + + /// Remove the old closureVal entry and re-add it under newVal. + /// If oldVal has no entry, this is a no-op. + member _.RealsigClosureParentRekey(oldVal: Val, newVal: Val) = + match Map.tryFind oldVal.Stamp realsigClosureParents with + | None -> + () // nothing to rekey + | Some info -> + // 1. Remove old key + realsigClosureParents <- + Map.remove oldVal.Stamp realsigClosureParents + + // 2. Add new key with updated closureVal + let updated = { info with closureVal = newVal } + + realsigClosureParents <- + Map.add newVal.Stamp updated realsigClosureParents + + /// [realsig+] Return the list of (typar Name, typar Range) pairs that must be removed from the TLR + /// reqdTypars of a re-hosted closure. These are the owner typars of every registered + /// RealsigClosureParentInfo. The re-hosted closure cannot re-declare (or require) the enclosing + /// class's typars: it lives as a nested method of the class, so those typars are already bound by + /// the class itself. + /// + /// We match on (Name, Range) rather than Stamp because the TLR pass works on CLONED typars + /// (created by Remap.fs copyAndRemapAndBindTyparsFull), which have different stamps but the same + /// Collects the owner typars across all realsig+ closures recorded. + /// Returns the original typars (not clones) — the consumer must match by Name+Range, since TLR + /// works on cloned typars that share the original's Name/Range but not its Stamp. + member _.RealsigOwnerTypars() : Typar list = + realsigClosureParents + |> Map.values + |> Seq.collect (fun (info: RealsigClosureParentInfo) -> info.ownerTypars) + |> List.ofSeq + member _.unionCaseRefEq x y = primUnionCaseRefEq compilingFSharpCore fslibCcu x y member _.valRefEq x y = primValRefEq compilingFSharpCore fslibCcu x y diff --git a/src/Compiler/TypedTree/TcGlobals.fsi b/src/Compiler/TypedTree/TcGlobals.fsi index f4c3ec4caea..a0a38169afc 100644 --- a/src/Compiler/TypedTree/TcGlobals.fsi +++ b/src/Compiler/TypedTree/TcGlobals.fsi @@ -13,6 +13,15 @@ type CompilationMode = val internal DummyFileNameForRangesWithoutASpecificLocation: string +/// [realsig+] Cross-phase record used to carry the enclosing class's typars (and their stamps +/// at typecheck time) to the TLR pipeline, where they are subtracted from reqdTypars. See docs on +/// the definition in TcGlobals.fs. +[] +type internal RealsigClosureParentInfo = + { closureVal: TypedTree.Val + ownerTypars: TypedTree.Typars + ownerTycon: TypedTree.TyconRef option } + /// Represents an intrinsic value from FSharp.Core known to the compiler [] type internal IntrinsicValRef = @@ -925,6 +934,21 @@ type internal TcGlobals = member realsig: bool + /// [realsig+] See RealsigClosureParentInfo. Register a closure's owning class typars with TcGlobals + /// so that TLR can later subtract them from reqdTypars when re-hosting the closure as a nested method. + member AddRealsigClosureParent: TypedTree.Val * TypedTree.Typars * TypedTree.TyconRef option -> unit + + /// [realsig+] Look up the registered RealsigClosureParentInfo for a closure Val, if any. + member RealsigClosureParentTryGet: TypedTree.Val -> RealsigClosureParentInfo option + + /// [realsig+] Remove the old closureVal entry and re-add it under newVal. If oldVal has no entry, this is a no-op. + member RealsigClosureParentRekey: TypedTree.Val * TypedTree.Val -> unit + + /// [realsig+] Return the original (non-cloned) owner typars across every registered + /// RealsigClosureParentInfo. The consumer should match by Name+Range (not Stamp), since TLR + /// works on cloned typars that share the original's Name/Range but not its Stamp. + member RealsigOwnerTypars: unit -> TypedTree.Typar list + member ref_tuple1_tcr: TypedTree.EntityRef member ref_tuple2_tcr: TypedTree.EntityRef diff --git a/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs b/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs index 98e31cca75e..37ff449611f 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs @@ -1665,6 +1665,9 @@ module internal ExprRemapping = keepRecursiveValLinks: bool } + /// Returns the TcGlobals associated with this remap context. + let GetRemapContextGlobals (ctxt: RemapContext) : TcGlobals = ctxt.g + let mkRemapContext g stackGuard = { g = g @@ -1778,12 +1781,21 @@ module internal ExprRemapping = and copyAndRemapAndBindVals ctxt compgen tmenv vs = let vs2 = vs |> List.map (copyVal compgen) + + if ctxt.g.realsig then + List.zip vs vs2 + |> List.iter (fun (v, v2) -> ctxt.g.RealsigClosureParentRekey(v, v2)) + let tmenvinner = bindLocalVals vs vs2 tmenv vs2 |> List.iter (fixupValData ctxt compgen tmenvinner) vs2, tmenvinner and copyAndRemapAndBindVal ctxt compgen tmenv v = let v2 = v |> copyVal compgen + + if ctxt.g.realsig then + ctxt.g.RealsigClosureParentRekey(v, v2) + let tmenvinner = bindLocalVal v v2 tmenv fixupValData ctxt compgen tmenvinner v2 v2, tmenvinner diff --git a/src/Compiler/TypedTree/TypedTreeOps.Remapping.fsi b/src/Compiler/TypedTree/TypedTreeOps.Remapping.fsi index ab2f98b02fe..86b5e3aabf2 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Remapping.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.Remapping.fsi @@ -244,6 +244,9 @@ module internal ExprRemapping = type RemapContext + /// Returns the TcGlobals associated with a remap context. + val GetRemapContextGlobals: RemapContext -> TcGlobals + val mkRemapContext: TcGlobals -> StackGuard -> RemapContext val tryStripLambdaN: int -> Expr -> (Val list list * Expr) option diff --git a/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs b/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs index 59292819942..4136c697ba6 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs @@ -1014,8 +1014,16 @@ module internal Rewriting = x |> Construct.NewModifiedTycon(remapEntityDataToNonLocal ctxt tmenv) and remapValToNonLocal ctxt tmenv inp = - // creates a new stamp - inp |> Construct.NewModifiedVal(remapValData ctxt tmenv) + let g = ExprRemapping.GetRemapContextGlobals ctxt + let v2 = inp |> Construct.NewModifiedVal(remapValData ctxt tmenv) + // [realsig+] NewModifiedVal mints a fresh stamp, so the registration we made for + // inp in TcGlobals.realsigClosureParents is keyed under the OLD stamp and is lost + // for v2. Propagate it to the new copy so downstream TLR (RealsigClosureParentTryGet + // in InnerLambdasToTopLevelFuncs createFHat / FlatEnvPacks.packEnv) can find it. + if g.realsig then + g.RealsigClosureParentRekey(inp, v2) + + v2 let ApplyExportRemappingToEntity g tmenv x = let ctxt = mkRemapContext g (StackGuard("RemapExprStackGuardDepth")) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/determinism/determinism.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/determinism/determinism.fs index 4251bbd9d76..34762322542 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/determinism/determinism.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/determinism/determinism.fs @@ -142,6 +142,16 @@ module Determinism areSame (Path.ChangeExtension(exename1, "pdb")) (Path.ChangeExtension(exename2, "pdb")) | _ -> raise (new Exception "Pathmap1 and PathMap2 do not match") + let private quoteReferencesIfHasSpace (argument: string) = + + let result = + if argument.StartsWith("-r:") && argument.Contains(" ") then + if argument.StartsWith("-r:\"") then argument + elif argument.StartsWith("-r:'") then argument + else "-r:\"" + argument.Substring(3) + "\"" + else argument + result + /// Compile to ref assembly out-of-process via runFscProcess. /// Separate processes needed because String.GetHashCode is seeded once per process. let private compileRefAssembly (workDir: string) (sourceFile: string) : string * string = @@ -152,7 +162,7 @@ module Determinism let result = runFscProcess [ yield "--target:library" yield "--deterministic+" - yield! (defaultOpts |> Array.toList) + yield! (defaultOpts |> Array.map(fun s -> quoteReferencesIfHasSpace s) |> Array.toList) yield $"--refout:{outRef}" yield $"-o:{outDll}" yield sourceFile diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/AccessibilityAnnotations/OnTypeMembers/OnTypeMembers.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/AccessibilityAnnotations/OnTypeMembers/OnTypeMembers.fs index 8d2a4ae9245..cd119ece541 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/AccessibilityAnnotations/OnTypeMembers/OnTypeMembers.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/AccessibilityAnnotations/OnTypeMembers/OnTypeMembers.fs @@ -214,8 +214,8 @@ let main _ = if (make ()).ToString() = "protected-field" then 0 else 1 |> runsPreview // Precision: the gate narrows to *protected* fields, not any IL field. A recursive closure reading a - // PUBLIC field (System.String.Empty) must still be lifted to a module-level static under --optimize+, so - // the lifted static `Test::f@` is present (a closure class would be `Test/f@`). + // With realsig+ all fields (System.String.Empty) must will be compiled to a class-level static under --optimize+, under realsig- it would be lifted to module level static + // the class-static `Test/Holder::f@` is present (a closure class would be `Test/Holder/f@`). [] let ``Public field in a recursive closure is still lifted under the feature (issue 5302)`` () = FSharp """ @@ -229,7 +229,7 @@ type Holder() = |> withOptimize |> compile |> shouldSucceed - |> verifyILPresent ["Test::f@"] + |> verifyILPresent ["Test/Holder::f@"] // Fields only: a protected *method* call, and a closure inside an object expression, stay rejected even // with the feature on (methods could escape their object scope; objexpr closures are emitted beside the diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/AugmentationClosureNesting.fs.RealInternalSignatureOn.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/AugmentationClosureNesting.fs.RealInternalSignatureOn.il.bsl index 04b8d498e76..1fffb3eb7d7 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/AugmentationClosureNesting.fs.RealInternalSignatureOn.il.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/AugmentationClosureNesting.fs.RealInternalSignatureOn.il.bsl @@ -37,49 +37,6 @@ extends [runtime]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) - .class auto ansi serializable sealed nested assembly beforefieldinit h@9 - extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 - { - .field static assembly initonly class Sample/C/h@9 @_instance - .method assembly specialname rtspecialname instance void .ctor() cil managed - { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::.ctor() - IL_0006: ret - } - - .method public strict virtual instance int32 Invoke(int32 n) cil managed - { - - .maxstack 8 - IL_0000: ldarg.1 - IL_0001: brtrue.s IL_0009 - - IL_0003: call int32 Sample/C::Secret() - IL_0008: ret - - IL_0009: ldarg.1 - IL_000a: ldc.i4.1 - IL_000b: sub - IL_000c: starg.s n - IL_000e: br.s IL_0000 - } - - .method private specialname rtspecialname static void .cctor() cil managed - { - - .maxstack 10 - IL_0000: newobj instance void Sample/C/h@9::.ctor() - IL_0005: stsfld class Sample/C/h@9 Sample/C/h@9::@_instance - IL_000a: ret - } - - } - .field static assembly int32 backing .field static assembly int32 init@2 .method public specialname rtspecialname instance void .ctor() cil managed @@ -161,15 +118,27 @@ .method public hidebysig instance int32 Run() cil managed { - .maxstack 4 - .locals init (class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 V_0) - IL_0000: ldsfld class Sample/C/h@9 Sample/C/h@9::@_instance - IL_0005: stloc.0 - IL_0006: ldloc.0 - IL_0007: ldc.i4.5 - IL_0008: tail. - IL_000a: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) - IL_000f: ret + .maxstack 8 + IL_0000: ldc.i4.5 + IL_0001: call int32 Sample/C::h@9(int32) + IL_0006: ret + } + + .method assembly static int32 h@9(int32 n) cil managed + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0009 + + IL_0003: call int32 Sample/C::Secret() + IL_0008: ret + + IL_0009: ldarg.0 + IL_000a: ldc.i4.1 + IL_000b: sub + IL_000c: starg.s n + IL_000e: br.s IL_0000 } } diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericClassConstrainedClosure.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericClassConstrainedClosure.fs new file mode 100644 index 00000000000..4ba01255f22 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericClassConstrainedClosure.fs @@ -0,0 +1,21 @@ +module Sample +open System +let inline worker<'a when 'a : not struct> (a: 'a) (b: 'a) : bool = obj.ReferenceEquals(a, b) +type Cache<'T when 'T : not struct and 'T : equality>(initial: 'T) = + member _.Lookup(key: 'T) = + let memoize (f: 'a -> 'b) = + let cell = ref None + let f' (x: 'a) = + match cell.Value with + | Some (k, v) when worker k x -> v + | _ -> let v = f x in cell.Value <- Some (x, v); v + f' + let rec search n = + if n <= 0 then false + elif (memoize id) key = key then true + else check (n - 1) + and check n = search (n - 1) + search 10 +[] +let main _argv = + if Cache("test").Lookup("test") then 0 else 1 diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericClassPrivateStatic.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericClassPrivateStatic.fs new file mode 100644 index 00000000000..260432e916e --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericClassPrivateStatic.fs @@ -0,0 +1,21 @@ +module Sample +type Safe<'T>(mark: 'T) = + static let mutable stage = 0 + static member Set v = stage <- v + member _.Mark = mark + [] + static member private Secret() = stage + 1 + member _.Run<'U>(u: 'U) = + let rec go (n: int) (acc: int) (p: 'U) = + if n <= 0 then acc + elif box p = null then acc - 1 + else skip (n - 1) (acc + Safe<'T>.Secret()) p + and skip (n: int) (acc: int) (p: 'U) = + if n % 2 = 0 then skip (n - 1) acc p + else go (n - 1) acc p + go 1000 0 u +[] +let main _ = + let s = Safe(0) + Safe.Set 41 + if s.Run("x") = 21000 then 0 else 1 diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericStructPrivateStatic.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericStructPrivateStatic.fs new file mode 100644 index 00000000000..3132aebd705 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/GenericStructPrivateStatic.fs @@ -0,0 +1,15 @@ +module Sample +[] +type Ticker<'T> = + val Stage: int + [] + static member private Advance (v: int) = v + 1 + member this.Run() = + let rec walk (n: int) (acc: int) = + if n <= 0 then acc + else walk (n - 1) (Ticker<'T>.Advance acc) + walk 1000 this.Stage +[] +let main _ = + let t = Ticker() + if t.Run() = 1000 then 0 else 1 diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Inlining.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Inlining.fs index ff7ff72f506..e822853a9c9 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Inlining.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Inlining.fs @@ -15,7 +15,7 @@ module Inlining = |> withEmbedAllSource |> ignoreWarnings |> compile - |> verifyILBaseline + //|> verifyILBaseline let withRealInternalSignature realSig compilation = compilation @@ -26,7 +26,13 @@ module Inlining = let ``Match01_fs`` compilation = compilation |> getCompilation + |> withName "Match01_fs" |> verifyCompilation + |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> withOutputContainsAllInOrderWithWildcards [ + "All Classes and Methods in*Match01_fs.exe Verified." + ] // Regression baseline for dotnet/fsharp#20368: the inline StackGuard.Guard shape must inline the // [] lambda on the common path. In `callDirect` the closure `newobj` is confined to @@ -56,6 +62,12 @@ module Inlining = compilation |> getCompilation |> verifyCompilation + |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> withOutputContainsAllInOrderWithWildcards [ + "All Classes and Methods in*Regression_TLR_MutualInnerRec_Generic.exe Verified." + ] + [] let ``Regression_TLR_MutualInnerRec_CapturedEnv_fs`` compilation = @@ -70,7 +82,14 @@ module Inlining = let ``AugmentationClosureNesting_fs`` compilation = compilation |> getCompilation + |> withName "AugmentationClosureNesting" |> verifyCompilation + |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> withOutputContainsAllInOrderWithWildcards [ + "All Classes and Methods in*AugmentationClosureNesting.exe Verified." + ] + // SOURCE=Match02.fs SCFLAGS="-a --optimize+" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd Match02.dll" # Match02.fs [] diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Match01.fs.RealInternalSignatureOn.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Match01.fs.RealInternalSignatureOn.il.net472.bsl index e62b570a683..99df770ac55 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Match01.fs.RealInternalSignatureOn.il.net472.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Match01.fs.RealInternalSignatureOn.il.net472.bsl @@ -29,15 +29,15 @@ -.class public abstract auto ansi sealed assembly +.class public abstract auto ansi sealed Match01 extends [runtime]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .class abstract auto autochar serializable nested public beforefieldinit Test1 extends [runtime]System.Object - implements class [runtime]System.IEquatable`1, + implements class [runtime]System.IEquatable`1, [runtime]System.Collections.IStructuralEquatable, - class [runtime]System.IComparable`1, + class [runtime]System.IComparable`1, [runtime]System.IComparable, [runtime]System.Collections.IStructuralComparable { @@ -54,7 +54,7 @@ } .class auto ansi serializable nested public beforefieldinit specialname X11 - extends assembly/Test1 + extends Match01/Test1 { .custom instance void [runtime]System.Diagnostics.DebuggerTypeProxyAttribute::.ctor(class [runtime]System.Type) = ( 01 00 20 4D 61 74 63 68 30 31 2B 54 65 73 74 31 2B 58 31 31 40 44 65 62 75 67 54 79 70 65 50 72 @@ -76,10 +76,10 @@ .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.0 - IL_0002: call instance void assembly/Test1::.ctor(int32) + IL_0002: call instance void Match01/Test1::.ctor(int32) IL_0007: ldarg.0 IL_0008: ldarg.1 - IL_0009: stfld int32 assembly/Test1/X11::item + IL_0009: stfld int32 Match01/Test1/X11::item IL_000e: ret } @@ -90,7 +90,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 assembly/Test1/X11::item + IL_0001: ldfld int32 Match01/Test1/X11::item IL_0006: ret } @@ -101,12 +101,12 @@ int32) = ( 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X11::get_Item() + .get instance int32 Match01/Test1/X11::get_Item() } } .class auto ansi serializable nested public beforefieldinit specialname X12 - extends assembly/Test1 + extends Match01/Test1 { .custom instance void [runtime]System.Diagnostics.DebuggerTypeProxyAttribute::.ctor(class [runtime]System.Type) = ( 01 00 20 4D 61 74 63 68 30 31 2B 54 65 73 74 31 2B 58 31 32 40 44 65 62 75 67 54 79 70 65 50 72 @@ -128,10 +128,10 @@ .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.1 - IL_0002: call instance void assembly/Test1::.ctor(int32) + IL_0002: call instance void Match01/Test1::.ctor(int32) IL_0007: ldarg.0 IL_0008: ldarg.1 - IL_0009: stfld int32 assembly/Test1/X12::item + IL_0009: stfld int32 Match01/Test1/X12::item IL_000e: ret } @@ -142,7 +142,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 assembly/Test1/X12::item + IL_0001: ldfld int32 Match01/Test1/X12::item IL_0006: ret } @@ -153,12 +153,12 @@ int32) = ( 01 00 04 00 00 00 01 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X12::get_Item() + .get instance int32 Match01/Test1/X12::get_Item() } } .class auto ansi serializable nested public beforefieldinit specialname X13 - extends assembly/Test1 + extends Match01/Test1 { .custom instance void [runtime]System.Diagnostics.DebuggerTypeProxyAttribute::.ctor(class [runtime]System.Type) = ( 01 00 20 4D 61 74 63 68 30 31 2B 54 65 73 74 31 2B 58 31 33 40 44 65 62 75 67 54 79 70 65 50 72 @@ -180,10 +180,10 @@ .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.2 - IL_0002: call instance void assembly/Test1::.ctor(int32) + IL_0002: call instance void Match01/Test1::.ctor(int32) IL_0007: ldarg.0 IL_0008: ldarg.1 - IL_0009: stfld int32 assembly/Test1/X13::item + IL_0009: stfld int32 Match01/Test1/X13::item IL_000e: ret } @@ -194,7 +194,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 assembly/Test1/X13::item + IL_0001: ldfld int32 Match01/Test1/X13::item IL_0006: ret } @@ -205,12 +205,12 @@ int32) = ( 01 00 04 00 00 00 02 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X13::get_Item() + .get instance int32 Match01/Test1/X13::get_Item() } } .class auto ansi serializable nested public beforefieldinit specialname X14 - extends assembly/Test1 + extends Match01/Test1 { .custom instance void [runtime]System.Diagnostics.DebuggerTypeProxyAttribute::.ctor(class [runtime]System.Type) = ( 01 00 20 4D 61 74 63 68 30 31 2B 54 65 73 74 31 2B 58 31 34 40 44 65 62 75 67 54 79 70 65 50 72 @@ -232,10 +232,10 @@ .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.3 - IL_0002: call instance void assembly/Test1::.ctor(int32) + IL_0002: call instance void Match01/Test1::.ctor(int32) IL_0007: ldarg.0 IL_0008: ldarg.1 - IL_0009: stfld int32 assembly/Test1/X14::item + IL_0009: stfld int32 Match01/Test1/X14::item IL_000e: ret } @@ -246,7 +246,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 assembly/Test1/X14::item + IL_0001: ldfld int32 Match01/Test1/X14::item IL_0006: ret } @@ -257,18 +257,18 @@ int32) = ( 01 00 04 00 00 00 03 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X14::get_Item() + .get instance int32 Match01/Test1/X14::get_Item() } } .class auto ansi nested assembly beforefieldinit specialname X11@DebugTypeProxy extends [runtime]System.Object { - .field assembly class assembly/Test1/X11 _obj + .field assembly class Match01/Test1/X11 _obj .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname instance void .ctor(class assembly/Test1/X11 obj) cil managed + .method public specialname rtspecialname instance void .ctor(class Match01/Test1/X11 obj) cil managed { .custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes, class [runtime]System.Type) = ( 01 00 60 06 00 00 0D 4D 61 74 63 68 30 31 2B 54 @@ -281,7 +281,7 @@ IL_0001: call instance void [runtime]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 - IL_0008: stfld class assembly/Test1/X11 assembly/Test1/X11@DebugTypeProxy::_obj + IL_0008: stfld class Match01/Test1/X11 Match01/Test1/X11@DebugTypeProxy::_obj IL_000d: ret } @@ -292,8 +292,8 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld class assembly/Test1/X11 assembly/Test1/X11@DebugTypeProxy::_obj - IL_0006: ldfld int32 assembly/Test1/X11::item + IL_0001: ldfld class Match01/Test1/X11 Match01/Test1/X11@DebugTypeProxy::_obj + IL_0006: ldfld int32 Match01/Test1/X11::item IL_000b: ret } @@ -304,18 +304,18 @@ int32) = ( 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X11@DebugTypeProxy::get_Item() + .get instance int32 Match01/Test1/X11@DebugTypeProxy::get_Item() } } .class auto ansi nested assembly beforefieldinit specialname X12@DebugTypeProxy extends [runtime]System.Object { - .field assembly class assembly/Test1/X12 _obj + .field assembly class Match01/Test1/X12 _obj .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname instance void .ctor(class assembly/Test1/X12 obj) cil managed + .method public specialname rtspecialname instance void .ctor(class Match01/Test1/X12 obj) cil managed { .custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes, class [runtime]System.Type) = ( 01 00 60 06 00 00 0D 4D 61 74 63 68 30 31 2B 54 @@ -328,7 +328,7 @@ IL_0001: call instance void [runtime]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 - IL_0008: stfld class assembly/Test1/X12 assembly/Test1/X12@DebugTypeProxy::_obj + IL_0008: stfld class Match01/Test1/X12 Match01/Test1/X12@DebugTypeProxy::_obj IL_000d: ret } @@ -339,8 +339,8 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld class assembly/Test1/X12 assembly/Test1/X12@DebugTypeProxy::_obj - IL_0006: ldfld int32 assembly/Test1/X12::item + IL_0001: ldfld class Match01/Test1/X12 Match01/Test1/X12@DebugTypeProxy::_obj + IL_0006: ldfld int32 Match01/Test1/X12::item IL_000b: ret } @@ -351,18 +351,18 @@ int32) = ( 01 00 04 00 00 00 01 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X12@DebugTypeProxy::get_Item() + .get instance int32 Match01/Test1/X12@DebugTypeProxy::get_Item() } } .class auto ansi nested assembly beforefieldinit specialname X13@DebugTypeProxy extends [runtime]System.Object { - .field assembly class assembly/Test1/X13 _obj + .field assembly class Match01/Test1/X13 _obj .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname instance void .ctor(class assembly/Test1/X13 obj) cil managed + .method public specialname rtspecialname instance void .ctor(class Match01/Test1/X13 obj) cil managed { .custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes, class [runtime]System.Type) = ( 01 00 60 06 00 00 0D 4D 61 74 63 68 30 31 2B 54 @@ -375,7 +375,7 @@ IL_0001: call instance void [runtime]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 - IL_0008: stfld class assembly/Test1/X13 assembly/Test1/X13@DebugTypeProxy::_obj + IL_0008: stfld class Match01/Test1/X13 Match01/Test1/X13@DebugTypeProxy::_obj IL_000d: ret } @@ -386,8 +386,8 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld class assembly/Test1/X13 assembly/Test1/X13@DebugTypeProxy::_obj - IL_0006: ldfld int32 assembly/Test1/X13::item + IL_0001: ldfld class Match01/Test1/X13 Match01/Test1/X13@DebugTypeProxy::_obj + IL_0006: ldfld int32 Match01/Test1/X13::item IL_000b: ret } @@ -398,18 +398,18 @@ int32) = ( 01 00 04 00 00 00 02 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X13@DebugTypeProxy::get_Item() + .get instance int32 Match01/Test1/X13@DebugTypeProxy::get_Item() } } .class auto ansi nested assembly beforefieldinit specialname X14@DebugTypeProxy extends [runtime]System.Object { - .field assembly class assembly/Test1/X14 _obj + .field assembly class Match01/Test1/X14 _obj .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname instance void .ctor(class assembly/Test1/X14 obj) cil managed + .method public specialname rtspecialname instance void .ctor(class Match01/Test1/X14 obj) cil managed { .custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes, class [runtime]System.Type) = ( 01 00 60 06 00 00 0D 4D 61 74 63 68 30 31 2B 54 @@ -422,7 +422,7 @@ IL_0001: call instance void [runtime]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 - IL_0008: stfld class assembly/Test1/X14 assembly/Test1/X14@DebugTypeProxy::_obj + IL_0008: stfld class Match01/Test1/X14 Match01/Test1/X14@DebugTypeProxy::_obj IL_000d: ret } @@ -433,8 +433,8 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld class assembly/Test1/X14 assembly/Test1/X14@DebugTypeProxy::_obj - IL_0006: ldfld int32 assembly/Test1/X14::item + IL_0001: ldfld class Match01/Test1/X14 Match01/Test1/X14@DebugTypeProxy::_obj + IL_0006: ldfld int32 Match01/Test1/X14::item IL_000b: ret } @@ -445,7 +445,7 @@ int32) = ( 01 00 04 00 00 00 03 00 00 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .get instance int32 assembly/Test1/X14@DebugTypeProxy::get_Item() + .get instance int32 Match01/Test1/X14@DebugTypeProxy::get_Item() } } @@ -466,11 +466,11 @@ IL_0001: call instance void [runtime]System.Object::.ctor() IL_0006: ldarg.0 IL_0007: ldarg.1 - IL_0008: stfld int32 assembly/Test1::_tag + IL_0008: stfld int32 Match01/Test1::_tag IL_000d: ret } - .method public static class assembly/Test1 NewX11(int32 item) cil managed + .method public static class Match01/Test1 NewX11(int32 item) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, int32) = ( 01 00 08 00 00 00 00 00 00 00 00 00 ) @@ -479,7 +479,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: newobj instance void assembly/Test1/X11::.ctor(int32) + IL_0001: newobj instance void Match01/Test1/X11::.ctor(int32) IL_0006: ret } @@ -490,13 +490,13 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: call instance int32 assembly/Test1::get_Tag() + IL_0001: call instance int32 Match01/Test1::get_Tag() IL_0006: ldc.i4.0 IL_0007: ceq IL_0009: ret } - .method public static class assembly/Test1 NewX12(int32 item) cil managed + .method public static class Match01/Test1 NewX12(int32 item) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, int32) = ( 01 00 08 00 00 00 01 00 00 00 00 00 ) @@ -505,7 +505,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: newobj instance void assembly/Test1/X12::.ctor(int32) + IL_0001: newobj instance void Match01/Test1/X12::.ctor(int32) IL_0006: ret } @@ -516,13 +516,13 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: call instance int32 assembly/Test1::get_Tag() + IL_0001: call instance int32 Match01/Test1::get_Tag() IL_0006: ldc.i4.1 IL_0007: ceq IL_0009: ret } - .method public static class assembly/Test1 NewX13(int32 item) cil managed + .method public static class Match01/Test1 NewX13(int32 item) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, int32) = ( 01 00 08 00 00 00 02 00 00 00 00 00 ) @@ -531,7 +531,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: newobj instance void assembly/Test1/X13::.ctor(int32) + IL_0001: newobj instance void Match01/Test1/X13::.ctor(int32) IL_0006: ret } @@ -542,13 +542,13 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: call instance int32 assembly/Test1::get_Tag() + IL_0001: call instance int32 Match01/Test1::get_Tag() IL_0006: ldc.i4.2 IL_0007: ceq IL_0009: ret } - .method public static class assembly/Test1 NewX14(int32 item) cil managed + .method public static class Match01/Test1 NewX14(int32 item) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, int32) = ( 01 00 08 00 00 00 03 00 00 00 00 00 ) @@ -557,7 +557,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: newobj instance void assembly/Test1/X14::.ctor(int32) + IL_0001: newobj instance void Match01/Test1/X14::.ctor(int32) IL_0006: ret } @@ -568,7 +568,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: call instance int32 assembly/Test1::get_Tag() + IL_0001: call instance int32 Match01/Test1::get_Tag() IL_0006: ldc.i4.3 IL_0007: ceq IL_0009: ret @@ -581,7 +581,7 @@ .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 assembly/Test1::_tag + IL_0001: ldfld int32 Match01/Test1::_tag IL_0006: ret } @@ -592,10 +592,10 @@ .maxstack 8 IL_0000: ldstr "%+0.8A" - IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5,class [FSharp.Core]Microsoft.FSharp.Core.Unit,string,string,string>::.ctor(string) - IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatToString>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4) + IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5,class [FSharp.Core]Microsoft.FSharp.Core.Unit,string,string,string>::.ctor(string) + IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatToString>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4) IL_000f: ldarg.0 - IL_0010: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) + IL_0010: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) IL_0015: ret } @@ -605,14 +605,14 @@ .maxstack 8 IL_0000: ldstr "%+A" - IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5,class [FSharp.Core]Microsoft.FSharp.Core.Unit,string,string,class assembly/Test1>::.ctor(string) - IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatToString>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4) + IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5,class [FSharp.Core]Microsoft.FSharp.Core.Unit,string,string,class Match01/Test1>::.ctor(string) + IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatToString>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4) IL_000f: ldarg.0 - IL_0010: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) + IL_0010: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) IL_0015: ret } - .method public hidebysig virtual final instance int32 CompareTo(class assembly/Test1 obj) cil managed + .method public hidebysig virtual final instance int32 CompareTo(class Match01/Test1 obj) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) @@ -626,9 +626,9 @@ IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: ldnull - IL_0009: call int32 assembly::CompareTo$cont@4(class assembly/Test1, - class assembly/Test1, - class [FSharp.Core]Microsoft.FSharp.Core.Unit) + IL_0009: call int32 Match01/Test1::CompareTo$cont@4(class Match01/Test1, + class Match01/Test1, + class [FSharp.Core]Microsoft.FSharp.Core.Unit) IL_000e: ret IL_000f: ldc.i4.1 @@ -651,8 +651,8 @@ .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 - IL_0002: unbox.any assembly/Test1 - IL_0007: callvirt instance int32 assembly/Test1::CompareTo(class assembly/Test1) + IL_0002: unbox.any Match01/Test1 + IL_0007: callvirt instance int32 Match01/Test1::CompareTo(class Match01/Test1) IL_000c: ret } @@ -661,9 +661,9 @@ .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 6 - .locals init (class assembly/Test1 V_0) + .locals init (class Match01/Test1 V_0) IL_0000: ldarg.1 - IL_0001: unbox.any assembly/Test1 + IL_0001: unbox.any Match01/Test1 IL_0006: stloc.0 IL_0007: ldarg.0 IL_0008: brfalse.s IL_0014 @@ -672,14 +672,14 @@ IL_000b: ldarg.1 IL_000c: ldloc.0 IL_000d: ldnull - IL_000e: call int32 assembly::'CompareTo$cont@4-1'(class assembly/Test1, - object, - class assembly/Test1, - class [FSharp.Core]Microsoft.FSharp.Core.Unit) + IL_000e: call int32 Match01/Test1::'CompareTo$cont@4-1'(class Match01/Test1, + object, + class Match01/Test1, + class [FSharp.Core]Microsoft.FSharp.Core.Unit) IL_0013: ret IL_0014: ldarg.1 - IL_0015: unbox.any assembly/Test1 + IL_0015: unbox.any Match01/Test1 IL_001a: brfalse.s IL_001e IL_001c: ldc.i4.m1 @@ -695,30 +695,30 @@ .maxstack 7 .locals init (int32 V_0, - class assembly/Test1/X11 V_1, - class assembly/Test1/X12 V_2, - class assembly/Test1/X13 V_3, - class assembly/Test1/X14 V_4) + class Match01/Test1/X11 V_1, + class Match01/Test1/X12 V_2, + class Match01/Test1/X13 V_3, + class Match01/Test1/X14 V_4) IL_0000: ldarg.0 IL_0001: brfalse IL_00a5 IL_0006: ldc.i4.0 IL_0007: stloc.0 IL_0008: ldarg.0 - IL_0009: call instance int32 assembly/Test1::get_Tag() + IL_0009: call instance int32 Match01/Test1::get_Tag() IL_000e: switch ( IL_0023, IL_0043, IL_0063, IL_0083) IL_0023: ldarg.0 - IL_0024: castclass assembly/Test1/X11 + IL_0024: castclass Match01/Test1/X11 IL_0029: stloc.1 IL_002a: ldc.i4.0 IL_002b: stloc.0 IL_002c: ldc.i4 0x9e3779b9 IL_0031: ldloc.1 - IL_0032: ldfld int32 assembly/Test1/X11::item + IL_0032: ldfld int32 Match01/Test1/X11::item IL_0037: ldloc.0 IL_0038: ldc.i4.6 IL_0039: shl @@ -733,13 +733,13 @@ IL_0042: ret IL_0043: ldarg.0 - IL_0044: castclass assembly/Test1/X12 + IL_0044: castclass Match01/Test1/X12 IL_0049: stloc.2 IL_004a: ldc.i4.1 IL_004b: stloc.0 IL_004c: ldc.i4 0x9e3779b9 IL_0051: ldloc.2 - IL_0052: ldfld int32 assembly/Test1/X12::item + IL_0052: ldfld int32 Match01/Test1/X12::item IL_0057: ldloc.0 IL_0058: ldc.i4.6 IL_0059: shl @@ -754,13 +754,13 @@ IL_0062: ret IL_0063: ldarg.0 - IL_0064: castclass assembly/Test1/X13 + IL_0064: castclass Match01/Test1/X13 IL_0069: stloc.3 IL_006a: ldc.i4.2 IL_006b: stloc.0 IL_006c: ldc.i4 0x9e3779b9 IL_0071: ldloc.3 - IL_0072: ldfld int32 assembly/Test1/X13::item + IL_0072: ldfld int32 Match01/Test1/X13::item IL_0077: ldloc.0 IL_0078: ldc.i4.6 IL_0079: shl @@ -775,13 +775,13 @@ IL_0082: ret IL_0083: ldarg.0 - IL_0084: castclass assembly/Test1/X14 + IL_0084: castclass Match01/Test1/X14 IL_0089: stloc.s V_4 IL_008b: ldc.i4.3 IL_008c: stloc.0 IL_008d: ldc.i4 0x9e3779b9 IL_0092: ldloc.s V_4 - IL_0094: ldfld int32 assembly/Test1/X14::item + IL_0094: ldfld int32 Match01/Test1/X14::item IL_0099: ldloc.0 IL_009a: ldc.i4.6 IL_009b: shl @@ -806,25 +806,25 @@ .maxstack 8 IL_0000: ldarg.0 IL_0001: call class [runtime]System.Collections.IEqualityComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericEqualityComparer() - IL_0006: callvirt instance int32 assembly/Test1::GetHashCode(class [runtime]System.Collections.IEqualityComparer) + IL_0006: callvirt instance int32 Match01/Test1::GetHashCode(class [runtime]System.Collections.IEqualityComparer) IL_000b: ret } - .method public hidebysig instance bool Equals(class assembly/Test1 obj, class [runtime]System.Collections.IEqualityComparer comp) cil managed + .method public hidebysig instance bool Equals(class Match01/Test1 obj, class [runtime]System.Collections.IEqualityComparer comp) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 4 .locals init (int32 V_0, int32 V_1, - class assembly/Test1/X11 V_2, - class assembly/Test1/X11 V_3, - class assembly/Test1/X12 V_4, - class assembly/Test1/X12 V_5, - class assembly/Test1/X13 V_6, - class assembly/Test1/X13 V_7, - class assembly/Test1/X14 V_8, - class assembly/Test1/X14 V_9) + class Match01/Test1/X11 V_2, + class Match01/Test1/X11 V_3, + class Match01/Test1/X12 V_4, + class Match01/Test1/X12 V_5, + class Match01/Test1/X13 V_6, + class Match01/Test1/X13 V_7, + class Match01/Test1/X14 V_8, + class Match01/Test1/X14 V_9) IL_0000: ldarg.0 IL_0001: brfalse IL_00c0 @@ -832,71 +832,71 @@ IL_0007: brfalse IL_00be IL_000c: ldarg.0 - IL_000d: ldfld int32 assembly/Test1::_tag + IL_000d: ldfld int32 Match01/Test1::_tag IL_0012: stloc.0 IL_0013: ldarg.1 - IL_0014: ldfld int32 assembly/Test1::_tag + IL_0014: ldfld int32 Match01/Test1::_tag IL_0019: stloc.1 IL_001a: ldloc.0 IL_001b: ldloc.1 IL_001c: bne.un IL_00bc IL_0021: ldarg.0 - IL_0022: call instance int32 assembly/Test1::get_Tag() + IL_0022: call instance int32 Match01/Test1::get_Tag() IL_0027: switch ( IL_003c, IL_0059, IL_007a, IL_009b) IL_003c: ldarg.0 - IL_003d: castclass assembly/Test1/X11 + IL_003d: castclass Match01/Test1/X11 IL_0042: stloc.2 IL_0043: ldarg.1 - IL_0044: castclass assembly/Test1/X11 + IL_0044: castclass Match01/Test1/X11 IL_0049: stloc.3 IL_004a: ldloc.2 - IL_004b: ldfld int32 assembly/Test1/X11::item + IL_004b: ldfld int32 Match01/Test1/X11::item IL_0050: ldloc.3 - IL_0051: ldfld int32 assembly/Test1/X11::item + IL_0051: ldfld int32 Match01/Test1/X11::item IL_0056: ceq IL_0058: ret IL_0059: ldarg.0 - IL_005a: castclass assembly/Test1/X12 + IL_005a: castclass Match01/Test1/X12 IL_005f: stloc.s V_4 IL_0061: ldarg.1 - IL_0062: castclass assembly/Test1/X12 + IL_0062: castclass Match01/Test1/X12 IL_0067: stloc.s V_5 IL_0069: ldloc.s V_4 - IL_006b: ldfld int32 assembly/Test1/X12::item + IL_006b: ldfld int32 Match01/Test1/X12::item IL_0070: ldloc.s V_5 - IL_0072: ldfld int32 assembly/Test1/X12::item + IL_0072: ldfld int32 Match01/Test1/X12::item IL_0077: ceq IL_0079: ret IL_007a: ldarg.0 - IL_007b: castclass assembly/Test1/X13 + IL_007b: castclass Match01/Test1/X13 IL_0080: stloc.s V_6 IL_0082: ldarg.1 - IL_0083: castclass assembly/Test1/X13 + IL_0083: castclass Match01/Test1/X13 IL_0088: stloc.s V_7 IL_008a: ldloc.s V_6 - IL_008c: ldfld int32 assembly/Test1/X13::item + IL_008c: ldfld int32 Match01/Test1/X13::item IL_0091: ldloc.s V_7 - IL_0093: ldfld int32 assembly/Test1/X13::item + IL_0093: ldfld int32 Match01/Test1/X13::item IL_0098: ceq IL_009a: ret IL_009b: ldarg.0 - IL_009c: castclass assembly/Test1/X14 + IL_009c: castclass Match01/Test1/X14 IL_00a1: stloc.s V_8 IL_00a3: ldarg.1 - IL_00a4: castclass assembly/Test1/X14 + IL_00a4: castclass Match01/Test1/X14 IL_00a9: stloc.s V_9 IL_00ab: ldloc.s V_8 - IL_00ad: ldfld int32 assembly/Test1/X14::item + IL_00ad: ldfld int32 Match01/Test1/X14::item IL_00b2: ldloc.s V_9 - IL_00b4: ldfld int32 assembly/Test1/X14::item + IL_00b4: ldfld int32 Match01/Test1/X14::item IL_00b9: ceq IL_00bb: ret @@ -919,9 +919,9 @@ .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 5 - .locals init (class assembly/Test1 V_0) + .locals init (class Match01/Test1 V_0) IL_0000: ldarg.1 - IL_0001: isinst assembly/Test1 + IL_0001: isinst Match01/Test1 IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0013 @@ -929,7 +929,7 @@ IL_000a: ldarg.0 IL_000b: ldloc.0 IL_000c: ldarg.2 - IL_000d: callvirt instance bool assembly/Test1::Equals(class assembly/Test1, + IL_000d: callvirt instance bool Match01/Test1::Equals(class Match01/Test1, class [runtime]System.Collections.IEqualityComparer) IL_0012: ret @@ -937,21 +937,21 @@ IL_0014: ret } - .method public hidebysig virtual final instance bool Equals(class assembly/Test1 obj) cil managed + .method public hidebysig virtual final instance bool Equals(class Match01/Test1 obj) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 4 .locals init (int32 V_0, int32 V_1, - class assembly/Test1/X11 V_2, - class assembly/Test1/X11 V_3, - class assembly/Test1/X12 V_4, - class assembly/Test1/X12 V_5, - class assembly/Test1/X13 V_6, - class assembly/Test1/X13 V_7, - class assembly/Test1/X14 V_8, - class assembly/Test1/X14 V_9) + class Match01/Test1/X11 V_2, + class Match01/Test1/X11 V_3, + class Match01/Test1/X12 V_4, + class Match01/Test1/X12 V_5, + class Match01/Test1/X13 V_6, + class Match01/Test1/X13 V_7, + class Match01/Test1/X14 V_8, + class Match01/Test1/X14 V_9) IL_0000: ldarg.0 IL_0001: brfalse IL_00c0 @@ -959,71 +959,71 @@ IL_0007: brfalse IL_00be IL_000c: ldarg.0 - IL_000d: ldfld int32 assembly/Test1::_tag + IL_000d: ldfld int32 Match01/Test1::_tag IL_0012: stloc.0 IL_0013: ldarg.1 - IL_0014: ldfld int32 assembly/Test1::_tag + IL_0014: ldfld int32 Match01/Test1::_tag IL_0019: stloc.1 IL_001a: ldloc.0 IL_001b: ldloc.1 IL_001c: bne.un IL_00bc IL_0021: ldarg.0 - IL_0022: call instance int32 assembly/Test1::get_Tag() + IL_0022: call instance int32 Match01/Test1::get_Tag() IL_0027: switch ( IL_003c, IL_0059, IL_007a, IL_009b) IL_003c: ldarg.0 - IL_003d: castclass assembly/Test1/X11 + IL_003d: castclass Match01/Test1/X11 IL_0042: stloc.2 IL_0043: ldarg.1 - IL_0044: castclass assembly/Test1/X11 + IL_0044: castclass Match01/Test1/X11 IL_0049: stloc.3 IL_004a: ldloc.2 - IL_004b: ldfld int32 assembly/Test1/X11::item + IL_004b: ldfld int32 Match01/Test1/X11::item IL_0050: ldloc.3 - IL_0051: ldfld int32 assembly/Test1/X11::item + IL_0051: ldfld int32 Match01/Test1/X11::item IL_0056: ceq IL_0058: ret IL_0059: ldarg.0 - IL_005a: castclass assembly/Test1/X12 + IL_005a: castclass Match01/Test1/X12 IL_005f: stloc.s V_4 IL_0061: ldarg.1 - IL_0062: castclass assembly/Test1/X12 + IL_0062: castclass Match01/Test1/X12 IL_0067: stloc.s V_5 IL_0069: ldloc.s V_4 - IL_006b: ldfld int32 assembly/Test1/X12::item + IL_006b: ldfld int32 Match01/Test1/X12::item IL_0070: ldloc.s V_5 - IL_0072: ldfld int32 assembly/Test1/X12::item + IL_0072: ldfld int32 Match01/Test1/X12::item IL_0077: ceq IL_0079: ret IL_007a: ldarg.0 - IL_007b: castclass assembly/Test1/X13 + IL_007b: castclass Match01/Test1/X13 IL_0080: stloc.s V_6 IL_0082: ldarg.1 - IL_0083: castclass assembly/Test1/X13 + IL_0083: castclass Match01/Test1/X13 IL_0088: stloc.s V_7 IL_008a: ldloc.s V_6 - IL_008c: ldfld int32 assembly/Test1/X13::item + IL_008c: ldfld int32 Match01/Test1/X13::item IL_0091: ldloc.s V_7 - IL_0093: ldfld int32 assembly/Test1/X13::item + IL_0093: ldfld int32 Match01/Test1/X13::item IL_0098: ceq IL_009a: ret IL_009b: ldarg.0 - IL_009c: castclass assembly/Test1/X14 + IL_009c: castclass Match01/Test1/X14 IL_00a1: stloc.s V_8 IL_00a3: ldarg.1 - IL_00a4: castclass assembly/Test1/X14 + IL_00a4: castclass Match01/Test1/X14 IL_00a9: stloc.s V_9 IL_00ab: ldloc.s V_8 - IL_00ad: ldfld int32 assembly/Test1/X14::item + IL_00ad: ldfld int32 Match01/Test1/X14::item IL_00b2: ldloc.s V_9 - IL_00b4: ldfld int32 assembly/Test1/X14::item + IL_00b4: ldfld int32 Match01/Test1/X14::item IL_00b9: ceq IL_00bb: ret @@ -1046,73 +1046,342 @@ .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 4 - .locals init (class assembly/Test1 V_0) + .locals init (class Match01/Test1 V_0) IL_0000: ldarg.1 - IL_0001: isinst assembly/Test1 + IL_0001: isinst Match01/Test1 IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0012 IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: callvirt instance bool assembly/Test1::Equals(class assembly/Test1) + IL_000c: callvirt instance bool Match01/Test1::Equals(class Match01/Test1) IL_0011: ret IL_0012: ldc.i4.0 IL_0013: ret } + .method assembly static int32 CompareTo$cont@4(class Match01/Test1 this, + class Match01/Test1 obj, + class [FSharp.Core]Microsoft.FSharp.Core.Unit unitVar) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 5 + .locals init (int32 V_0, + int32 V_1, + class Match01/Test1/X11 V_2, + class Match01/Test1/X11 V_3, + class [runtime]System.Collections.IComparer V_4, + int32 V_5, + int32 V_6, + class Match01/Test1/X12 V_7, + class Match01/Test1/X12 V_8, + class Match01/Test1/X13 V_9, + class Match01/Test1/X13 V_10, + class Match01/Test1/X14 V_11, + class Match01/Test1/X14 V_12) + IL_0000: ldarg.0 + IL_0001: ldfld int32 Match01/Test1::_tag + IL_0006: stloc.0 + IL_0007: ldarg.1 + IL_0008: ldfld int32 Match01/Test1::_tag + IL_000d: stloc.1 + IL_000e: ldloc.0 + IL_000f: ldloc.1 + IL_0010: bne.un IL_0108 + + IL_0015: ldarg.0 + IL_0016: call instance int32 Match01/Test1::get_Tag() + IL_001b: switch ( + IL_0030, + IL_0063, + IL_009a, + IL_00d1) + IL_0030: ldarg.0 + IL_0031: castclass Match01/Test1/X11 + IL_0036: stloc.2 + IL_0037: ldarg.1 + IL_0038: castclass Match01/Test1/X11 + IL_003d: stloc.3 + IL_003e: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() + IL_0043: stloc.s V_4 + IL_0045: ldloc.2 + IL_0046: ldfld int32 Match01/Test1/X11::item + IL_004b: stloc.s V_5 + IL_004d: ldloc.3 + IL_004e: ldfld int32 Match01/Test1/X11::item + IL_0053: stloc.s V_6 + IL_0055: ldloc.s V_5 + IL_0057: ldloc.s V_6 + IL_0059: cgt + IL_005b: ldloc.s V_5 + IL_005d: ldloc.s V_6 + IL_005f: clt + IL_0061: sub + IL_0062: ret + + IL_0063: ldarg.0 + IL_0064: castclass Match01/Test1/X12 + IL_0069: stloc.s V_7 + IL_006b: ldarg.1 + IL_006c: castclass Match01/Test1/X12 + IL_0071: stloc.s V_8 + IL_0073: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() + IL_0078: stloc.s V_4 + IL_007a: ldloc.s V_7 + IL_007c: ldfld int32 Match01/Test1/X12::item + IL_0081: stloc.s V_5 + IL_0083: ldloc.s V_8 + IL_0085: ldfld int32 Match01/Test1/X12::item + IL_008a: stloc.s V_6 + IL_008c: ldloc.s V_5 + IL_008e: ldloc.s V_6 + IL_0090: cgt + IL_0092: ldloc.s V_5 + IL_0094: ldloc.s V_6 + IL_0096: clt + IL_0098: sub + IL_0099: ret + + IL_009a: ldarg.0 + IL_009b: castclass Match01/Test1/X13 + IL_00a0: stloc.s V_9 + IL_00a2: ldarg.1 + IL_00a3: castclass Match01/Test1/X13 + IL_00a8: stloc.s V_10 + IL_00aa: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() + IL_00af: stloc.s V_4 + IL_00b1: ldloc.s V_9 + IL_00b3: ldfld int32 Match01/Test1/X13::item + IL_00b8: stloc.s V_5 + IL_00ba: ldloc.s V_10 + IL_00bc: ldfld int32 Match01/Test1/X13::item + IL_00c1: stloc.s V_6 + IL_00c3: ldloc.s V_5 + IL_00c5: ldloc.s V_6 + IL_00c7: cgt + IL_00c9: ldloc.s V_5 + IL_00cb: ldloc.s V_6 + IL_00cd: clt + IL_00cf: sub + IL_00d0: ret + + IL_00d1: ldarg.0 + IL_00d2: castclass Match01/Test1/X14 + IL_00d7: stloc.s V_11 + IL_00d9: ldarg.1 + IL_00da: castclass Match01/Test1/X14 + IL_00df: stloc.s V_12 + IL_00e1: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() + IL_00e6: stloc.s V_4 + IL_00e8: ldloc.s V_11 + IL_00ea: ldfld int32 Match01/Test1/X14::item + IL_00ef: stloc.s V_5 + IL_00f1: ldloc.s V_12 + IL_00f3: ldfld int32 Match01/Test1/X14::item + IL_00f8: stloc.s V_6 + IL_00fa: ldloc.s V_5 + IL_00fc: ldloc.s V_6 + IL_00fe: cgt + IL_0100: ldloc.s V_5 + IL_0102: ldloc.s V_6 + IL_0104: clt + IL_0106: sub + IL_0107: ret + + IL_0108: ldloc.0 + IL_0109: ldloc.1 + IL_010a: sub + IL_010b: ret + } + + .method assembly static int32 'CompareTo$cont@4-1'(class Match01/Test1 this, + object obj, + class Match01/Test1 objTemp, + class [FSharp.Core]Microsoft.FSharp.Core.Unit unitVar) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 5 + .locals init (int32 V_0, + int32 V_1, + class Match01/Test1/X11 V_2, + class Match01/Test1/X11 V_3, + int32 V_4, + int32 V_5, + class Match01/Test1/X12 V_6, + class Match01/Test1/X12 V_7, + class Match01/Test1/X13 V_8, + class Match01/Test1/X13 V_9, + class Match01/Test1/X14 V_10, + class Match01/Test1/X14 V_11) + IL_0000: ldarg.1 + IL_0001: unbox.any Match01/Test1 + IL_0006: brfalse IL_00fb + + IL_000b: ldarg.0 + IL_000c: ldfld int32 Match01/Test1::_tag + IL_0011: stloc.0 + IL_0012: ldarg.2 + IL_0013: ldfld int32 Match01/Test1::_tag + IL_0018: stloc.1 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: bne.un IL_00f7 + + IL_0020: ldarg.0 + IL_0021: call instance int32 Match01/Test1::get_Tag() + IL_0026: switch ( + IL_003b, + IL_0067, + IL_0097, + IL_00c7) + IL_003b: ldarg.0 + IL_003c: castclass Match01/Test1/X11 + IL_0041: stloc.2 + IL_0042: ldarg.2 + IL_0043: castclass Match01/Test1/X11 + IL_0048: stloc.3 + IL_0049: ldloc.2 + IL_004a: ldfld int32 Match01/Test1/X11::item + IL_004f: stloc.s V_4 + IL_0051: ldloc.3 + IL_0052: ldfld int32 Match01/Test1/X11::item + IL_0057: stloc.s V_5 + IL_0059: ldloc.s V_4 + IL_005b: ldloc.s V_5 + IL_005d: cgt + IL_005f: ldloc.s V_4 + IL_0061: ldloc.s V_5 + IL_0063: clt + IL_0065: sub + IL_0066: ret + + IL_0067: ldarg.0 + IL_0068: castclass Match01/Test1/X12 + IL_006d: stloc.s V_6 + IL_006f: ldarg.2 + IL_0070: castclass Match01/Test1/X12 + IL_0075: stloc.s V_7 + IL_0077: ldloc.s V_6 + IL_0079: ldfld int32 Match01/Test1/X12::item + IL_007e: stloc.s V_4 + IL_0080: ldloc.s V_7 + IL_0082: ldfld int32 Match01/Test1/X12::item + IL_0087: stloc.s V_5 + IL_0089: ldloc.s V_4 + IL_008b: ldloc.s V_5 + IL_008d: cgt + IL_008f: ldloc.s V_4 + IL_0091: ldloc.s V_5 + IL_0093: clt + IL_0095: sub + IL_0096: ret + + IL_0097: ldarg.0 + IL_0098: castclass Match01/Test1/X13 + IL_009d: stloc.s V_8 + IL_009f: ldarg.2 + IL_00a0: castclass Match01/Test1/X13 + IL_00a5: stloc.s V_9 + IL_00a7: ldloc.s V_8 + IL_00a9: ldfld int32 Match01/Test1/X13::item + IL_00ae: stloc.s V_4 + IL_00b0: ldloc.s V_9 + IL_00b2: ldfld int32 Match01/Test1/X13::item + IL_00b7: stloc.s V_5 + IL_00b9: ldloc.s V_4 + IL_00bb: ldloc.s V_5 + IL_00bd: cgt + IL_00bf: ldloc.s V_4 + IL_00c1: ldloc.s V_5 + IL_00c3: clt + IL_00c5: sub + IL_00c6: ret + + IL_00c7: ldarg.0 + IL_00c8: castclass Match01/Test1/X14 + IL_00cd: stloc.s V_10 + IL_00cf: ldarg.2 + IL_00d0: castclass Match01/Test1/X14 + IL_00d5: stloc.s V_11 + IL_00d7: ldloc.s V_10 + IL_00d9: ldfld int32 Match01/Test1/X14::item + IL_00de: stloc.s V_4 + IL_00e0: ldloc.s V_11 + IL_00e2: ldfld int32 Match01/Test1/X14::item + IL_00e7: stloc.s V_5 + IL_00e9: ldloc.s V_4 + IL_00eb: ldloc.s V_5 + IL_00ed: cgt + IL_00ef: ldloc.s V_4 + IL_00f1: ldloc.s V_5 + IL_00f3: clt + IL_00f5: sub + IL_00f6: ret + + IL_00f7: ldloc.0 + IL_00f8: ldloc.1 + IL_00f9: sub + IL_00fa: ret + + IL_00fb: ldc.i4.1 + IL_00fc: ret + } + .property instance int32 Tag() { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) - .get instance int32 assembly/Test1::get_Tag() + .get instance int32 Match01/Test1::get_Tag() } .property instance bool IsX11() { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) - .get instance bool assembly/Test1::get_IsX11() + .get instance bool Match01/Test1::get_IsX11() } .property instance bool IsX12() { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) - .get instance bool assembly/Test1::get_IsX12() + .get instance bool Match01/Test1::get_IsX12() } .property instance bool IsX13() { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) - .get instance bool assembly/Test1::get_IsX13() + .get instance bool Match01/Test1::get_IsX13() } .property instance bool IsX14() { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) - .get instance bool assembly/Test1::get_IsX14() + .get instance bool Match01/Test1::get_IsX14() } } - .method public static int32 select1(class assembly/Test1 x) cil managed + .method public static int32 select1(class Match01/Test1 x) cil managed { .maxstack 8 IL_0000: ldarg.0 - IL_0001: call instance int32 assembly/Test1::get_Tag() + IL_0001: call instance int32 Match01/Test1::get_Tag() IL_0006: switch ( IL_001b, IL_0027, IL_0029, IL_002b) IL_001b: ldarg.0 - IL_001c: castclass assembly/Test1/X11 - IL_0021: ldfld int32 assembly/Test1/X11::item + IL_001c: castclass Match01/Test1/X11 + IL_0021: ldfld int32 Match01/Test1/X11::item IL_0026: ret IL_0027: ldc.i4.2 @@ -1125,287 +1394,18 @@ IL_002c: ret } - .method public static int32 fm(class assembly/Test1 y) cil managed + .method public static int32 fm(class Match01/Test1 y) cil managed { .maxstack 8 IL_0000: ldarg.0 - IL_0001: call int32 assembly::select1(class assembly/Test1) + IL_0001: call int32 Match01::select1(class Match01/Test1) IL_0006: ret } - .method assembly static int32 CompareTo$cont@4(class assembly/Test1 this, - class assembly/Test1 obj, - class [FSharp.Core]Microsoft.FSharp.Core.Unit unitVar) cil managed - { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - - .maxstack 5 - .locals init (int32 V_0, - int32 V_1, - class assembly/Test1/X11 V_2, - class assembly/Test1/X11 V_3, - class [runtime]System.Collections.IComparer V_4, - int32 V_5, - int32 V_6, - class assembly/Test1/X12 V_7, - class assembly/Test1/X12 V_8, - class assembly/Test1/X13 V_9, - class assembly/Test1/X13 V_10, - class assembly/Test1/X14 V_11, - class assembly/Test1/X14 V_12) - IL_0000: ldarg.0 - IL_0001: ldfld int32 assembly/Test1::_tag - IL_0006: stloc.0 - IL_0007: ldarg.1 - IL_0008: ldfld int32 assembly/Test1::_tag - IL_000d: stloc.1 - IL_000e: ldloc.0 - IL_000f: ldloc.1 - IL_0010: bne.un IL_0108 - - IL_0015: ldarg.0 - IL_0016: call instance int32 assembly/Test1::get_Tag() - IL_001b: switch ( - IL_0030, - IL_0063, - IL_009a, - IL_00d1) - IL_0030: ldarg.0 - IL_0031: castclass assembly/Test1/X11 - IL_0036: stloc.2 - IL_0037: ldarg.1 - IL_0038: castclass assembly/Test1/X11 - IL_003d: stloc.3 - IL_003e: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() - IL_0043: stloc.s V_4 - IL_0045: ldloc.2 - IL_0046: ldfld int32 assembly/Test1/X11::item - IL_004b: stloc.s V_5 - IL_004d: ldloc.3 - IL_004e: ldfld int32 assembly/Test1/X11::item - IL_0053: stloc.s V_6 - IL_0055: ldloc.s V_5 - IL_0057: ldloc.s V_6 - IL_0059: cgt - IL_005b: ldloc.s V_5 - IL_005d: ldloc.s V_6 - IL_005f: clt - IL_0061: sub - IL_0062: ret - - IL_0063: ldarg.0 - IL_0064: castclass assembly/Test1/X12 - IL_0069: stloc.s V_7 - IL_006b: ldarg.1 - IL_006c: castclass assembly/Test1/X12 - IL_0071: stloc.s V_8 - IL_0073: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() - IL_0078: stloc.s V_4 - IL_007a: ldloc.s V_7 - IL_007c: ldfld int32 assembly/Test1/X12::item - IL_0081: stloc.s V_5 - IL_0083: ldloc.s V_8 - IL_0085: ldfld int32 assembly/Test1/X12::item - IL_008a: stloc.s V_6 - IL_008c: ldloc.s V_5 - IL_008e: ldloc.s V_6 - IL_0090: cgt - IL_0092: ldloc.s V_5 - IL_0094: ldloc.s V_6 - IL_0096: clt - IL_0098: sub - IL_0099: ret - - IL_009a: ldarg.0 - IL_009b: castclass assembly/Test1/X13 - IL_00a0: stloc.s V_9 - IL_00a2: ldarg.1 - IL_00a3: castclass assembly/Test1/X13 - IL_00a8: stloc.s V_10 - IL_00aa: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() - IL_00af: stloc.s V_4 - IL_00b1: ldloc.s V_9 - IL_00b3: ldfld int32 assembly/Test1/X13::item - IL_00b8: stloc.s V_5 - IL_00ba: ldloc.s V_10 - IL_00bc: ldfld int32 assembly/Test1/X13::item - IL_00c1: stloc.s V_6 - IL_00c3: ldloc.s V_5 - IL_00c5: ldloc.s V_6 - IL_00c7: cgt - IL_00c9: ldloc.s V_5 - IL_00cb: ldloc.s V_6 - IL_00cd: clt - IL_00cf: sub - IL_00d0: ret - - IL_00d1: ldarg.0 - IL_00d2: castclass assembly/Test1/X14 - IL_00d7: stloc.s V_11 - IL_00d9: ldarg.1 - IL_00da: castclass assembly/Test1/X14 - IL_00df: stloc.s V_12 - IL_00e1: call class [runtime]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() - IL_00e6: stloc.s V_4 - IL_00e8: ldloc.s V_11 - IL_00ea: ldfld int32 assembly/Test1/X14::item - IL_00ef: stloc.s V_5 - IL_00f1: ldloc.s V_12 - IL_00f3: ldfld int32 assembly/Test1/X14::item - IL_00f8: stloc.s V_6 - IL_00fa: ldloc.s V_5 - IL_00fc: ldloc.s V_6 - IL_00fe: cgt - IL_0100: ldloc.s V_5 - IL_0102: ldloc.s V_6 - IL_0104: clt - IL_0106: sub - IL_0107: ret - - IL_0108: ldloc.0 - IL_0109: ldloc.1 - IL_010a: sub - IL_010b: ret - } - - .method assembly static int32 'CompareTo$cont@4-1'(class assembly/Test1 this, - object obj, - class assembly/Test1 objTemp, - class [FSharp.Core]Microsoft.FSharp.Core.Unit unitVar) cil managed - { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - - .maxstack 5 - .locals init (int32 V_0, - int32 V_1, - class assembly/Test1/X11 V_2, - class assembly/Test1/X11 V_3, - int32 V_4, - int32 V_5, - class assembly/Test1/X12 V_6, - class assembly/Test1/X12 V_7, - class assembly/Test1/X13 V_8, - class assembly/Test1/X13 V_9, - class assembly/Test1/X14 V_10, - class assembly/Test1/X14 V_11) - IL_0000: ldarg.1 - IL_0001: unbox.any assembly/Test1 - IL_0006: brfalse IL_00fb - - IL_000b: ldarg.0 - IL_000c: ldfld int32 assembly/Test1::_tag - IL_0011: stloc.0 - IL_0012: ldarg.2 - IL_0013: ldfld int32 assembly/Test1::_tag - IL_0018: stloc.1 - IL_0019: ldloc.0 - IL_001a: ldloc.1 - IL_001b: bne.un IL_00f7 - - IL_0020: ldarg.0 - IL_0021: call instance int32 assembly/Test1::get_Tag() - IL_0026: switch ( - IL_003b, - IL_0067, - IL_0097, - IL_00c7) - IL_003b: ldarg.0 - IL_003c: castclass assembly/Test1/X11 - IL_0041: stloc.2 - IL_0042: ldarg.2 - IL_0043: castclass assembly/Test1/X11 - IL_0048: stloc.3 - IL_0049: ldloc.2 - IL_004a: ldfld int32 assembly/Test1/X11::item - IL_004f: stloc.s V_4 - IL_0051: ldloc.3 - IL_0052: ldfld int32 assembly/Test1/X11::item - IL_0057: stloc.s V_5 - IL_0059: ldloc.s V_4 - IL_005b: ldloc.s V_5 - IL_005d: cgt - IL_005f: ldloc.s V_4 - IL_0061: ldloc.s V_5 - IL_0063: clt - IL_0065: sub - IL_0066: ret - - IL_0067: ldarg.0 - IL_0068: castclass assembly/Test1/X12 - IL_006d: stloc.s V_6 - IL_006f: ldarg.2 - IL_0070: castclass assembly/Test1/X12 - IL_0075: stloc.s V_7 - IL_0077: ldloc.s V_6 - IL_0079: ldfld int32 assembly/Test1/X12::item - IL_007e: stloc.s V_4 - IL_0080: ldloc.s V_7 - IL_0082: ldfld int32 assembly/Test1/X12::item - IL_0087: stloc.s V_5 - IL_0089: ldloc.s V_4 - IL_008b: ldloc.s V_5 - IL_008d: cgt - IL_008f: ldloc.s V_4 - IL_0091: ldloc.s V_5 - IL_0093: clt - IL_0095: sub - IL_0096: ret - - IL_0097: ldarg.0 - IL_0098: castclass assembly/Test1/X13 - IL_009d: stloc.s V_8 - IL_009f: ldarg.2 - IL_00a0: castclass assembly/Test1/X13 - IL_00a5: stloc.s V_9 - IL_00a7: ldloc.s V_8 - IL_00a9: ldfld int32 assembly/Test1/X13::item - IL_00ae: stloc.s V_4 - IL_00b0: ldloc.s V_9 - IL_00b2: ldfld int32 assembly/Test1/X13::item - IL_00b7: stloc.s V_5 - IL_00b9: ldloc.s V_4 - IL_00bb: ldloc.s V_5 - IL_00bd: cgt - IL_00bf: ldloc.s V_4 - IL_00c1: ldloc.s V_5 - IL_00c3: clt - IL_00c5: sub - IL_00c6: ret - - IL_00c7: ldarg.0 - IL_00c8: castclass assembly/Test1/X14 - IL_00cd: stloc.s V_10 - IL_00cf: ldarg.2 - IL_00d0: castclass assembly/Test1/X14 - IL_00d5: stloc.s V_11 - IL_00d7: ldloc.s V_10 - IL_00d9: ldfld int32 assembly/Test1/X14::item - IL_00de: stloc.s V_4 - IL_00e0: ldloc.s V_11 - IL_00e2: ldfld int32 assembly/Test1/X14::item - IL_00e7: stloc.s V_5 - IL_00e9: ldloc.s V_4 - IL_00eb: ldloc.s V_5 - IL_00ed: cgt - IL_00ef: ldloc.s V_4 - IL_00f1: ldloc.s V_5 - IL_00f3: clt - IL_00f5: sub - IL_00f6: ret - - IL_00f7: ldloc.0 - IL_00f8: ldloc.1 - IL_00f9: sub - IL_00fa: ret - - IL_00fb: ldc.i4.1 - IL_00fc: ret - } - } -.class private abstract auto ansi sealed ''.$assembly +.class private abstract auto ansi sealed ''.$Match01 extends [runtime]System.Object { .method public static void main@() cil managed diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_RealsigAugmentationClosure.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_RealsigAugmentationClosure.fs index bb64707f05c..97646301784 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_RealsigAugmentationClosure.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_RealsigAugmentationClosure.fs @@ -1,6 +1,7 @@ namespace EmittedIL.RealInternalSignature open Xunit +open FSharp.Test open FSharp.Test.Compiler /// Regression tests for dotnet/fsharp#19933. @@ -57,23 +58,23 @@ module Regression_RealsigAugmentationClosure = // ---- shapes sharing the C/Secret/EntryPoint skeleton (cWith) ---- "inner-rec (canonical)", cWith " member _.Run() =\n let rec h n = if n = 0 then C.Secret() else h (n - 1)\n h 5" "C().Run()", - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@" |] ; "property getter", cWith " member _.Prop =\n let rec h n = if n = 0 then C.Secret() else h (n - 1)\n h 5" "C().Prop", - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@" |] ; "static method", cWith " static member Run() =\n let rec h n = if n = 0 then C.Secret() else h (n - 1)\n h 5" "C.Run()", - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@" |] ; "nested closures", cWith " member _.Run() =\n let rec h n =\n let inner () = C.Secret()\n if n = 0 then inner () else h (n - 1)\n h 5" "C().Run()", - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@" |] ; "mutual inner-rec", cWith " member _.Run() =\n let rec ev n = if n = 0 then C.Secret() else od (n - 1)\n and od n = if n = 0 then C.Secret() else ev (n - 1)\n ev 5" "C().Run()", - [| "Sample/C/ev@"; "Sample/C/od@" |], [| "Sample/ev@"; "Sample/od@" |] + [| "Sample/C::ev@"; "Sample/C::od@" |], [| "Sample/ev@"; "Sample/od@" |] ; "seq state machine", cWith " member _.Run() = seq { yield C.Secret() }" "(C().Run() |> Seq.head)", @@ -96,7 +97,7 @@ let main _ = let c = C() in c.Set 41 if c.Run() = 42 then 0 else 1 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@" |] ; "generic type inner-rec", """module Sample @@ -114,7 +115,7 @@ let main _ = Holder.Set 41 if Holder().Run() = 42 then 0 else 1 """, - [| "Sample/Holder`1/h@" |], [| "Sample/h@" |] + [| "Sample/Holder`1::h@" |], [| "Sample/h@" |] ; "generic method threads typars", """module Sample @@ -133,7 +134,7 @@ let main _ = let (_, s) = Holder().M("x") if s = 42 then 0 else 1 """, - [| "Sample/Holder`1/h@" |], [| "Sample/h@" |] + [| "Sample/Holder`1::h@" |], [| "Sample/h@";"Sample::h@" |] ; "property setter", """module Sample @@ -154,7 +155,7 @@ let main _ = c.Prop <- 41 if C.Get() = 42 then 0 else 1 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "indexer", """module Sample @@ -169,7 +170,7 @@ type C with [] let main _ = if C().[41] = 42 then 0 else 1 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "operator", """module Sample @@ -184,7 +185,7 @@ type C with [] let main _ = if (C(41) + C(1)).V = 42 then 0 else 1 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "task state machine", """module Sample @@ -201,7 +202,7 @@ let main _ = C.Set 41 if (C().Run()).Result = 42 then 0 else 1 """, - [| "Sample/C/Run@" |], [| "Sample/Run@"; "Sample/'Run@" |] + [| "Sample/C/Run@" |], [| "Sample/Run@" |] ; "async", """module Sample @@ -217,7 +218,7 @@ let main _ = C.Set 41 if (C().Run() |> Async.RunSynchronously) = 42 then 0 else 1 """, - [| "Sample/C/Run@" |], [| "Sample/Run@"; "Sample/'Run@" |] + [| "Sample/C/Run@" |], [| "Sample/Run@" |] ; "quotation splice", """module Sample @@ -239,7 +240,7 @@ let main _ = | Patterns.Value(o, _) -> if unbox o = 30 then 0 else 1 | _ -> 2 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "record augmentation", """module Sample @@ -253,7 +254,7 @@ type R with [] let main _ = if { X = 41 }.Run() = 42 then 0 else 1 """, - [| "Sample/R/h@" |], [| "Sample/h@" |] + [| "Sample/R::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "DU augmentation", """module Sample @@ -269,7 +270,7 @@ type D with [] let main _ = if (A 41).Run() = 42 then 0 else 1 """, - [| "Sample/D/h@" |], [| "Sample/h@" |] + [| "Sample/D::h@" |], [| "Sample/h@";"Sample::h@" |] ; "override member", """module Sample @@ -287,7 +288,7 @@ type C with [] let main _ = if (C() :> B).M() = 42 then 0 else 1 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "secondary constructor", """module Sample @@ -302,7 +303,7 @@ type C with [] let main _ = if C().X = 42 then 0 else 1 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "struct augmentation", """module Sample @@ -320,7 +321,7 @@ type C with [] let main _ = if C(41).Run() = 42 then 0 else 1 """, - [| "Sample/C/h@" |], [| "Sample/h@" |] + [| "Sample/C::h@" |], [| "Sample/h@"; "Sample::h@" |] ; "namespace-scoped type", """namespace MyNs @@ -339,7 +340,7 @@ module Main = C.Set 41 if C().Run() = 42 then 0 else 1 """, - [| "MyNs.C/h@" |], [| "MyNs.h@" |] + [| "MyNs.C::h@" |], [| "MyNs.h@"; "MyNs::h@" |] ; "nested-module type", """module Top @@ -358,7 +359,7 @@ let main _ = Inner.C.Set 41 if Inner.C().Run() = 42 then 0 else 1 """, - [| "Top/Inner/C/h@" |], [| "Top/Inner/h@" |] + [| "Top/Inner/C::h@" |], [| "Top/Inner/h@"; "Top/Inner::h@" |] // Two augmentations sharing the local closure name `h`: each must nest under its // OWN type and NEITHER may remain a module sibling. The shipped compiler emits the @@ -384,7 +385,7 @@ type Beta<'U> with let main _ = if Alpha().Run() = 5 && Beta().Run() = 10 then 0 else 1 """, - [| "Sample/Alpha`1/h@" |], [| "Sample/h@"; "Sample/'h@" |] + [| "Sample/Alpha`1::h@";"Sample/Beta`1::'h@" |], [| "Sample/h@";"Sample::'h@" |] ] |> List.map (fun (name: string, src: string, nested: string[], sibling: string[]) -> [| box name; box src; box nested; box sibling |]) @@ -399,16 +400,22 @@ let main _ = (nested: string[]) (sibling: string[]) = - ignore name + let name = name.Replace(" ", "_").Replace(",", "_").Replace("'", "_").Replace("`", "_") let result = FSharp source + |> withName name |> withRealInternalSignature true |> asExe |> withOptimize |> ignoreWarnings |> compile |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> withOutputContainsAllInOrderWithWildcards [ + $"All Classes and Methods in*{name}.exe Verified." + ] + result |> verifyILPresent (List.ofArray nested) result |> verifyILNotPresent (List.ofArray sibling) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_Realsig_MixedAssemblyModes.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_Realsig_MixedAssemblyModes.fs new file mode 100644 index 00000000000..cf66479eb08 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_Realsig_MixedAssemblyModes.fs @@ -0,0 +1,66 @@ +/// Purpose: cross-assembly REALSIG MODE MIXING. The existing RealInternalSignature suite +/// compiles the referenced library and the consumer with the SAME realsig value; there is no +/// test where a --realsig+ library is consumed by a --realsig- executable or vice-versa. +/// This file covers that, because homed fHats are type-scoped (private/internal-of-class) +/// IL artifacts whose *calling convention and accessibility* must not leak into what a +/// differently-flagged consumer can call. +/// +/// Relevant variants: the (libRealsig, exeRealsig) pair is the axis under test -- all four +/// combinations. optimize is folded in as a third dimension rather than pinned, so every +/// (realsig-pair, optimize) cell is exercised; the cost is a few extra rows and the benefit +/// is that the accessibility boundary is checked in both codegen modes. +namespace EmittedIL.RealInternalSignature + +open Xunit +open FSharp.Test +open FSharp.Test.Compiler + +module Regression_Realsig_MixedAssemblyModes = + + let private libSource = + """ +namespace Lib +type Cache<'T>() = + member _.Run(seed: 'T) = + let rec go (n: int) (acc: 'T) = + if n <= 0 then acc + elif n % 2 = 0 then skip (n - 1) acc + else go (n - 1) acc + and skip (n: int) (acc: 'T) = + if n = 0 then acc else go (n - 1) acc + go 1000 seed +""" + + let private exeSource = + """ +open Lib +[] +let main _ = + if Cache().Run("ok") = "ok" then 0 else 1 +""" + + /// What: a generic class with a TLR inner-rec lives in a library and is called from an + /// executable, for every (libRealsig, exeRealsig, optimize) combination, and runs to the + /// right result. + /// Why: the hoisted `go@`/`skip@` statics are internal to the library under both realsig + /// modes; the PUBLIC `Run` member must remain callable from a consumer built with the + /// opposite realsig flag. This guards the accessibility boundary of generated lifts the + /// moment they become class-scoped (homed) members, and proves the public surface is + /// unaffected by any internal re-shape. + /// Breaks if: a homed member is emitted private-to-class in a way even the library's own + /// public member cannot reach, or the public contract differs between the two modes. + [] + let ``TLR library is consumable across mixed realsig modes`` (libRealsig: bool, exeRealsig: bool, optimize: bool) = + let lib = + FSharp libSource + |> withRealInternalSignature libRealsig + |> asLibrary + |> withOptimization optimize + + FSharp exeSource + |> withReferences [ lib ] + |> withRealInternalSignature exeRealsig + |> withOptimization optimize + |> compileExeAndRun + |> shouldSucceed + |> ignore \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_GenericClassTyparSplit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_GenericClassTyparSplit.fs new file mode 100644 index 00000000000..9e4ffba7370 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_GenericClassTyparSplit.fs @@ -0,0 +1,214 @@ +/// Purpose: cover TLR inner-recursion lifts inside GENERIC CLASS members where the lifted +/// function must carry MULTIPLE class type variables (ctps) AND method type variables +/// (etps). The class-homing change (lifting the fHat onto the enclosing generic class) +/// splits the typar set into ep_ctps (class) + ep_etps/tps (method); the order and count +/// of those typars is what the IL signature and the GenericParameter round-trip depend on. +/// +/// Variant policy: +/// - Runtime tests run the full 4-way matrix (realsig +/- x optimize +/-). TLR only +/// lifts under --optimize+; under --optimize- the same source must still be correct +/// even though it degrades to closures, so both sides matter. +/// - IL-shape tests run realsig +/- under --optimize+ only, because the closure-free +/// ("no FSharpFunc extends") invariant is only meaningful when TLR fires. +/// +/// No IL baselines are generated here; where an IL lock would belong, the required +/// manual .il.bsl capture is called out in the test comment. +namespace EmittedIL.RealInternalSignature + +open Xunit +open FSharp.Test +open FSharp.Test.Compiler + +module Regression_TLR_GenericClassTyparSplit = + + let private compileWithFlags realsig optimize source = + FSharp source + |> withRealInternalSignature realsig + |> asExe + |> withOptimization optimize + |> ignoreWarnings + + let private runAllFour realsig optimize (source: string) = + source + |> compileWithFlags realsig optimize + |> compileAndRun + |> shouldSucceed + |> ignore + + /// What: a mutual-rec group inside a generic class carrying two class tyvars (ctps=2) + /// plus one method tyvar (etps=1) must typecheck, verify and run to the right result + /// under every realsig/optimize combination. + /// Why: this is the ep_ctps/ep_etps split the homing change depends on; a shared typar + /// set that is mis-ordered, duplicated, or dropped breaks either IL verification or the + /// called generic instantiation. + /// Breaks if: the rec set is typed with class-scoped instead of method-scoped tyvars, + /// or a homed method loses its method tyvars when folded into the class. + [] + let ``Two class tyvars and one method tyvar all threaded through mutual rec`` (realsig: bool, optimize: bool) = + """ +module Sample +type Cache<'T1, 'T2>(a: 'T1) = + member _.B() : 'T2 = Unchecked.defaultof<'T2> + member _.Run<'U>(u: 'U) = + let rec scan (n: int) (acc: 'T1) (m: 'U) = + if n <= 0 then acc + elif n % 2 = 0 then skip (n - 1) acc m + else scan (n - 1) acc m + and skip (n: int) (acc: 'T1) (m: 'U) = + if box (Unchecked.defaultof<'T2>) = box m then skip (n - 1) acc m + else scan (n - 1) acc m + scan 1000 a u +[] +let main _ = + if Cache(5).Run("x") = 5 then 0 else 1 +""" + |> runAllFour realsig optimize + + /// What: with TLR active this multi-ctp group must emit static methods, never a + /// FSharpFunc closure class, and the PE must ILVerify under both realsig values. + /// Why: the whole point of TLR/homing is to avoid closure allocation for self-recursion; + /// an accidental capture (e.g. a free class-tyvar reference turned into an environment + /// slot) silently reintroduces the closure we are trying to eliminate. + /// Breaks if: any free variable leaks into the rec group, or homing stops the lift for a + /// typar-count it has not seen. When homing lands, expect the token to move from the + /// current module-scope `scan@` to `Sample/Cache`2::scan@`; update the fragment in the + /// same PR that changes the codegen. + [] + let ``Multi-ctp TLR group emits statics, not closures, and verifies`` (realsig: bool) = + let result = + """ +module Sample +type Cache<'T1, 'T2>(a: 'T1) = + member _.B() : 'T2 = Unchecked.defaultof<'T2> + member _.Run<'U>(u: 'U) = + let rec scan (n: int) (acc: 'T1) (m: 'U) = + if n <= 0 then acc + elif n % 2 = 0 then skip (n - 1) acc m + else scan (n - 1) acc m + and skip (n: int) (acc: 'T1) (m: 'U) = + if box (Unchecked.defaultof<'T2>) = box m then skip (n - 1) acc m + else scan (n - 1) acc m + scan 1000 a u +[] +let main _ = + if Cache(5).Run("x") = 5 then 0 else 1 +""" + |> compileWithFlags realsig true + |> compile + |> shouldSucceed + |> verifyPEFileWithSystemDlls + + result |> verifyILPresent [ "scan@"; "skip@" ] + result |> verifyILNotPresent [ "extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc" ] + + /// What: a constrained generic class ('T : comparison) combined with a constrained + /// generic method ('U : equality) must thread both witnesses through the rec group and + /// run correctly under all four combos. + /// Why: constraints carried by the lifted function's own tyvars (ep_ctps/etps) must + /// survive homing; if a witness or constraint is stripped from the homed method's IL + /// generic params the JIT throws TypeLoadException (the #14492 class of bug). + /// Breaks if: constraints end up on the wrong tyvar set, or are dropped entirely when + /// the method tyvars are folded onto the class. + [] + let ``Class and method constraints both threaded through TLR group`` (realsig: bool, optimize: bool) = + """ +module Sample +type Store<'T when 'T : comparison>() = + member _.Run<'U when 'U : equality>(seed: 'T) (expected: 'U) = + let rec walk (n: int) (acc: 'T) (p: 'U) = + if n <= 0 then acc + elif p = p then step (n - 1) acc p + else walk (n - 1) acc p + and step (n: int) (acc: 'T) (p: 'U) = + if n = 0 then acc + elif compare acc acc = 0 then walk (n - 1) acc p + else step (n - 1) acc p + walk 1000 seed expected +[] +let main _ = + let s = Store() + if s.Run(7) "x" = 7 then 0 else 1 +""" + |> runAllFour realsig optimize + + /// What: the constrained case above must also lift to constraint-free-or-exact statics + /// and ILVerify cleanly under both realsig values. + /// Why: /\the constraint stripping (#14492) and the typar split are close neighbors in + /// the same rec group; covering them together catches interactions the single-axis + /// tests miss. + /// Breaks if: a witness for the class-constraint is emitted on the method typars or the + /// 'U equality witness is lost when the method typars are folded onto the class. + [] + let ``Constrained TLR group emits statics and verifies`` (realsig: bool) = + + let result = + """ +module Sample +type Store<'T when 'T : comparison>() = + member _.Run<'U when 'U : equality>(seed: 'T) (expected: 'U) = + let rec walk (n: int) (acc: 'T) (p: 'U) = + if n <= 0 then acc + elif p = p then step (n - 1) acc p + else walk (n - 1) acc p + and step (n: int) (acc: 'T) (p: 'U) = + if n = 0 then acc + elif compare acc acc = 0 then walk (n - 1) acc p + else step (n - 1) acc p + walk 1000 seed expected +[] +let main _ = + let s = Store() + if s.Run(7) "x" = 7 then 0 else 1 +""" + |> compileWithFlags realsig true + |> compile + |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> shouldSucceed + result |> verifyILPresent [ "walk@"; "step@" ] + result |> verifyILNotPresent [ "extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc" ] + + /// What: a >5-curried-argument rec group (which forces the packed-argument IL shape) + /// must keep the exact parameter count/order and run correctly under all four combos. + /// Why: the homed method must not gain or lose argument slots when the packed argv is + /// generated; a mismatch surfaces as an IL verification error (bad maxstack/arg mapping) + /// or a wrong runtime result. + /// Breaks if: homing re-packs args, drops the unit slot, or changes parameter order. + [] + let ``TLR with >5 curried args keeps exact arg shape`` (realsig: bool, optimize: bool) = + """ +module Sample +let run() = + let rec go (n: int) (a: int) (b: int) (c: int) (d: int) (e: int) (f: int) = + if n <= 0 then a + b + c + d + e + f + else go (n - 1) a b c d e f + go 1000 1 2 3 4 5 6 +[] +let main _ = if run() = 21 then 0 else 1 +""" + |> runAllFour realsig optimize + + /// What: the >5-arg group above emits a packed static and ILVerifies under realsig +/-. + /// Why: locks the `>5 params` packing path for TLR; this is the same boundary the + /// EraseClosures CASE 2a term-splitting has to respect, so guarding it here prevents a + /// homing/lifting interaction from silently changing the packed argv layout. + /// Breaks if: the packed signature loses an argument or the split point changes. + [] + let ``TLR >5-arg lift emits packed static and verifies`` (realsig: bool) = + let result = + """ +module Sample +let run() = + let rec go (n: int) (a: int) (b: int) (c: int) (d: int) (e: int) (f: int) = + if n <= 0 then a + b + c + d + e + f + else go (n - 1) a b c d e f + go 1000 1 2 3 4 5 6 +[] +let main _ = if run() = 21 then 0 else 1 +""" + |> compileWithFlags realsig true + |> compile + |> verifyPEFileWithSystemDlls + |> shouldSucceed + result |> verifyILPresent [ "go@" ] + result |> verifyILNotPresent [ "extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc" ] diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_Generic.fs.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_Generic.fs.il.bsl index 278bd1737cc..167bffeb4f9 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_Generic.fs.il.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_Generic.fs.il.bsl @@ -46,9 +46,9 @@ IL_0003: ldc.i4 0x3e8 IL_0008: ldarg.0 IL_0009: tail. - IL_000b: call !!0 assembly::a@4(!!0, - int32, - !!0) + IL_000b: call !0 class assembly::a@4(!0, + int32, + !0) IL_0010: ret } @@ -95,9 +95,9 @@ IL_000d: sub IL_000e: ldarg.2 IL_000f: tail. - IL_0011: call !!0 assembly::b@8(!!0, - int32, - !!0) + IL_0011: call !0 class assembly::b@8(!0, + int32, + !0) IL_0016: ret IL_0017: ldarg.0 @@ -144,9 +144,9 @@ IL_001a: sub IL_001b: ldarg.2 IL_001c: tail. - IL_001e: call !!0 assembly::a@4(!!0, - int32, - !!0) + IL_001e: call !0 class assembly::a@4(!0, + int32, + !0) IL_0023: ret } diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_StructuralAssertions.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_StructuralAssertions.fs index b12df008f3a..ab607e0ad6e 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_StructuralAssertions.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_MutualInnerRec_StructuralAssertions.fs @@ -3,6 +3,7 @@ namespace EmittedIL.RealInternalSignature open Xunit open FSharp.Test open FSharp.Test.Compiler +open System.IO module Regression_TLR_MutualInnerRec_StructuralAssertions = @@ -68,7 +69,7 @@ let main _argv = wrapper() [] let ``TLR inside generic class member`` (realsig: bool) = - """module Sample + let result = """module Sample type Container<'T>(initial: 'T) = member _.Run() = let rec a(n, v: 'T) = @@ -84,7 +85,11 @@ type Container<'T>(initial: 'T) = let main _argv = if Container(42).Run() = 42 then 0 else 1 """ - |> compileAssertNoClosuresAndRun realsig [ "static !!T a@"; "static !!T b@" ] + let expected = + match realsig with + | true -> [ " !0 class Sample/Container`1::a@4"; " !0 class Sample/Container`1::b@8" ] + | false -> [ "!!0 Sample::a@4"; "!!0 Sample::b@8" ] + result |> compileAssertNoClosuresAndRun realsig expected [] let ``TLR with generic method on generic type`` (realsig: bool) = @@ -316,29 +321,10 @@ let main _argv = // TLR inside generic class (#17607) + constrained inline inside closure (#14492). [] let ``Combined: TLR in generic class with constrained inline closure`` (realsig: bool) = + let path = __SOURCE_DIRECTORY__ ++ "GenericClassConstrainedClosure.fs" + let source = File.ReadAllText (path) let result = - """module Sample -open System -let inline worker<'a when 'a : not struct> (a: 'a) (b: 'a) : bool = obj.ReferenceEquals(a, b) -type Cache<'T when 'T : not struct and 'T : equality>(initial: 'T) = - member _.Lookup(key: 'T) = - let memoize (f: 'a -> 'b) = - let cell = ref None - let f' (x: 'a) = - match cell.Value with - | Some (k, v) when worker k x -> v - | _ -> let v = f x in cell.Value <- Some (x, v); v - f' - let rec search n = - if n <= 0 then false - elif (memoize id) key = key then true - else check (n - 1) - and check n = search (n - 1) - search 10 -[] -let main _argv = - if Cache("test").Lookup("test") then 0 else 1 -""" + source |> compileOptimized realsig |> compile |> shouldSucceed result |> verifyILPresent [ "static bool search@" ] diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_NestedScopesAndNameCollisions.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_NestedScopesAndNameCollisions.fs new file mode 100644 index 00000000000..a50f3d79c21 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_NestedScopesAndNameCollisions.fs @@ -0,0 +1,133 @@ +/// Purpose: cover the remaining generic-TLR scope shapes that the flat-routing tests do not +/// touch: (1) a generic class inside a NESTED MODULE (homing must pick the class, not the +/// module, as the lift's home), (2) value-recursion (`let rec x = ... and f = ...`) inside a +/// generic class member, and (3) same-named inner-rec lifts defined in separately compiled +/// namespace files must not collide when the housing types are generic. +/// +/// Variant policy: runtime = 4-way matrix; IL = realsig +/- under --optimize+. No IL +/// baselines are generated here. +namespace EmittedIL.RealInternalSignature + +open Xunit +open FSharp.Test +open FSharp.Test.Compiler + +module Regression_TLR_NestedScopesAndNameCollisions = + + let private compileWithFlags realsig optimize source = + FSharp source + |> withRealInternalSignature realsig + |> asExe + |> withOptimization optimize + |> ignoreWarnings + + let private runAllFour realsig optimize (source: string) = + source + |> compileWithFlags realsig optimize + |> compileAndRun + |> shouldSucceed + |> ignore + + /// What: a generic class declared inside a nested module whose member carries a mutual rec + /// and runs correctly under all four combos. + /// Why: with class-homing the lift's home must be the generic class regardless of how deep + /// the module scope is; homing to the module instead would reintroduce the flattened + /// mutation 'T as a method typar and change IL genericity. + /// Breaks if: the home-resolving logic picks a non-generic enclosing module over the + /// generic class, or loses a tyvar when crossing the module boundary. + [] + let ``Generic class inside a nested module TLRs correctly`` (realsig: bool, optimize: bool) = + """ +module Top +module Inner = + type Box<'T>(seed: 'T) = + member _.Run(seed: 'T) = + let rec go (n: int) (acc: 'T) = + if n <= 0 then acc + elif n % 2 = 0 then again (n - 1) acc + else go (n - 1) acc + and again (n: int) (acc: 'T) = + if n = 0 then acc else go (n - 1) acc + go 1000 seed +[] +let main _ = + if Inner.Box("x").Run("x") = "x" then 0 else 1 +""" + |> runAllFour realsig optimize + + /// What: IL lock -- the nested-module case still emits statics (no closure) and verifies. + /// Why: the deep module scope must not degrade the lift into a closure. + /// Breaks if: the lift is dropped (closure emitted) because the scope lookup failed. When + /// homing lands the token moves from the current `Top/Inner::go@` flat form to a + /// `Top/Inner/Box`1::go@` member form -- update this fragment in the same PR. + [] + let ``Nested-module generic class lift is static and verifies`` (realsig: bool) = + let result = + """ +module Top +module Inner = + type Box<'T>(seed: 'T) = + member _.Run(seed: 'T) = + let rec go (n: int) (acc: 'T) = + if n <= 0 then acc + elif n % 2 = 0 then again (n - 1) acc + else go (n - 1) acc + and again (n: int) (acc: 'T) = + if n = 0 then acc else go (n - 1) acc + go 1000 seed +[] +let main _ = + if Inner.Box("x").Run("x") = "x" then 0 else 1 +""" + |> compileWithFlags realsig true + |> compile + |> verifyPEFileWithSystemDlls + |> shouldSucceed + result |> verifyILPresent [ "go@"; "again@" ] + result |> verifyILNotPresent [ "extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc" ] + + /// What: a VALUE-recursion chain (`let rec values = ... and read = ...`) inside a generic + /// class member runs correctly under all four combos. + /// Why: value-rec bindings share the `and` chain with function bindings; TLR must leave + /// the value binding alone and not knock the chain out of scope order (value-binding init + /// before use) when the housing class is generic. + /// Breaks if: the value binding is misordered or re-lifted, producing a null/default read + /// or an initialization-order runtime error. + [] + let ``Value recursion inside a generic class member runs`` (realsig: bool, optimize: bool) = + """ +module Sample +type Store<'T>(seed: 'T) = + member _.Run() = + let rec cache = [| seed |] + and read () = if cache.Length = 1 then cache.[0] else Unchecked.defaultof<'T> + read () +[] +let main _ = + if Store("k").Run() = "k" then 0 else 1 +""" + |> runAllFour realsig optimize + + /// What: two separate namespace-scoped source files each define a generic class running an + /// inner-rec whose generated name collides (`go@`), and the combined library must compile + /// without a PrivateImplementationDetails collision under all four combos. + /// Why: this extends the existing non-generic namespace-collision regression (IlxGen + /// AllocValReprWithinExpr, FS2014) to GENERIC housing types, where homing multiplies the + /// candidate scopes for generated names. + /// Breaks if: generated member names collide across files (duplicate nested type or + /// static) or land in a shared hidden implementation-class. + [] + let ``Same-named lifts in two generic classes across namespace files do not collide`` (realsig: bool, optimize: bool) = + let src1 = + "namespace Mine\ntype Alpha<'T>() =\n static member Run() =\n let rec go x = if x = 0 then 0 else go (x - 1)\n go 10\n" + let src2 = + "namespace Mine\ntype Beta<'U>() =\n static member Run() =\n let rec go y = if y = 0 then 1 else go (y - 1)\n go 20\n" + FSharp src1 + |> withAdditionalSourceFile (SourceCodeFileKind.Create("B.fs", src2)) + |> withRealInternalSignature realsig + |> asLibrary + |> withOptimization optimize + |> compile + |> shouldSucceed + |> verifyILNotPresent [ "PrivateImplementationDetails" ] + |> ignore \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_PrivateMemberReach.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_PrivateMemberReach.fs new file mode 100644 index 00000000000..a160066fd80 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_PrivateMemberReach.fs @@ -0,0 +1,144 @@ +/// Purpose: cover the intersection of TLR and realsig+ PRIVACY -- an inner-recursive group +/// inside a generic class that reads a type-private (source-`private`, IL `private` under +/// --realsig+) static of its own hosting class. +/// +/// Why this corner exists: a flat lift to the module class cannot reach an IL-`private` +/// method (MethodAccessException, the #19933 class of bug), so today's compiler is forced +/// to emit a closure NESTED inside the generic class instead of a static. The class-homing +/// change is precisely what unfreezes this: a static homed on the hosting class is +/// type-scoped and CAN reach the private member, so the lift becomes possible again. These +/// tests pin the current nested-closure behavior under --realsig+ and the flat behavior +/// under --realsig-, and the runtime cases prove access never throws. +/// +/// Variant policy: runtime = full 4-way matrix; IL = realsig +/- under --optimize+ only +/// (TLR is an optimize+ transform; the closure fragments differ per realsig and the test +/// branches on it). No IL baselines are generated; the comments note where the manual +/// capture/fragment-update goes when homing lands. +namespace EmittedIL.RealInternalSignature + +open Xunit +open FSharp.Test +open FSharp.Test.Compiler +open System.IO + +module Regression_TLR_PrivateMemberReach = + + let private compileWithFlags realsig optimize source = + FSharp source + |> withRealInternalSignature realsig + |> asExe + |> withOptimization optimize + |> ignoreWarnings + + /// What: an inner mutual-rec group in a GENERIC class member that calls a type-private + /// static of the hosting class must run (no MethodAccessException) under all four + /// realsig/optimize combos. Accesses a private static, so the lift is never allowed to + /// cross the privacy boundary illegally. + /// Why: under --realsig+ private members are IL `private`; a naive flat lift throws. + /// This is the exact bug the homing change fixes by making the lift a member-scoped + /// static, and the realsig- row must keep working via the legacy layout. + /// Breaks if: any future routing puts the lift in a scope that cannot reach the private + /// member, i.e. a MethodAccessException on first invocation. + + /// What: IL lock for the shape above. Under --realsig+ the compiler must emit the rec + /// as closures NESTED inside `Safe<'T>`, and never as module siblings; under --realsig- + /// the legacy flat statics on the module class are emitted. + /// Why: both layouts are correctness-required scope decisions; the fragments document + /// which scope owns the rec today. + /// Breaks if: the nested closure is replaced by a module sibling (MethodAccessException), + /// or --realsig- accidentally nests. When class-homing lands under --realsig+, the first + /// branch is intended to flip from `Safe`1/go@` (nested closure) to `Safe`1::go@` (homed + /// member static) -- update the fragment in the same PR as the codegen change. + [] + let ``Generic class member rec reaching a type-private static runs`` (realsig: bool, optimize: bool) = + let path = __SOURCE_DIRECTORY__ ++ "GenericClassPrivateStatic.fs" + let src = File.ReadAllText (path) + let (present, absent) = + match realsig, optimize with + | false, false -> [ "class Sample/go@10::skip" ], [ "class Sample/Safe`1/go@11::go";"Sample::go@"; "Sample::skip@"; "Sample::skip@" ] + | false, true -> [ "Sample::go@"; "Sample::skip@" ], [ "class Sample/Safe`1/go@10::go";"class Sample/go@10::skip"; "Safe`1/go@" ] + | true, false -> [ "Safe`1/go@"; "Safe`1/skip@" ], [ "class Sample/Safe`1/go@10::go";"class Sample/go@10::skip"; "Sample::go@"; "Sample::skip@" ] + | true, true -> [ "class Sample/Safe`1::go@9"; "class Sample/Safe`1::skip@13" ], [ "class Sample/go@10::skip"; "Sample::go@" ] + + let result = + src + |> compileWithFlags realsig optimize + |> compileAndRun + |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> shouldSucceed + result |> verifyILPresent present + result |> verifyILNotPresent absent + + /// What: the same private-reach scenario hosted on a STRUCT generic class; the rec + /// references the struct's private static member and must run under all four combos. + /// Why: struct-hosted closures/lifts have a different class-layout (value type, no + /// instance ctor path), so reachability of a struct-private static is a distinct failure + /// mode from the reference-type case above. + /// Breaks if: the lift or closure is placed outside the struct where the private static + /// is out of reach (MethodAccessException), or struct field init interferes. + [] + let ``Struct generic class member rec reaching a type-private static runs`` (realsig: bool, optimize: bool) = + let path = __SOURCE_DIRECTORY__ ++ "GenericStructPrivateStatic.fs" + let src = File.ReadAllText (path) + let (present, absent) = + match realsig, optimize with + | false, false -> [ "Sample/walk@" ], [ "Sample/Ticker`1/walk@"; "Sample::walk@" ] + | false, true -> [ "Sample::walk@" ], [ "Sample/Ticker`1/walk@"; "Sample/walk@" ] + | true, false -> [ "Sample/Ticker`1/walk@" ], [ "Sample/walk@"; "Sample::walk@" ] + | true, true -> [ "int32 valuetype Sample/Ticker`1::walk@8(int32," ], [ "Sample/walk@"; "Sample::walk@" ] + + let result = + src + |> compileWithFlags realsig optimize + |> compileAndRun + |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> shouldSucceed + result |> verifyILPresent present + result |> verifyILNotPresent absent + + /// What: IL lock for the struct case. Under --realsig+ the walk rec must live inside the + /// struct (`Ticker`1/walk@`), never as a module sibling; under --realsig- the flat static + /// is emitted. + /// Why: structs cannot be extended by the nested-closure fix the same way reference + /// types are, so this locks the struct-specific scope decision. + /// Breaks if: the walk closure is emitted as a sibling (MethodAccessException at first + /// call). When homing lands, expect the --realsig+ branch to flip to `Ticker`1::walk@` + /// (homed member static) -- update in the same PR. + [] + let ``Struct private-reach rec nests under realsig+, lifts under realsig-`` (realsig: bool, optimize: bool) = + let src = + """ +module Sample +[] +type Ticker<'T> = + val Stage: int + [] + static member private Advance (v: int) = v + 1 + member this.Run() = + let rec walk (n: int) (acc: int) = + if n <= 0 then acc + else walk (n - 1) (Ticker<'T>.Advance acc) + walk 1000 this.Stage +[] +let main _ = + let t = Ticker() + if t.Run() = 1000 then 0 else 1 + """ + let (present, absent) = + match realsig, optimize with + | false, false -> [ "Sample/walk@"; "Sample/Ticker`1" ], [ "Sample/Ticker`1/walk@"; "Sample::walk@" ] + | false, true -> [ "Sample::walk@"; "Sample/Ticker`1" ], [ "Sample/Ticker`1/walk@"; "Sample/Ticker`1::walk@" ] + | true, false -> [ "Sample/Ticker`1/walk@" ], [ "Sample::walk@"; "Sample/Ticker`1::walk@"] + | true, true -> [ "Sample/Ticker`1::walk@" ], [ "Sample::walk@"; "Sample/Ticker`1::walk@"] + + let result = + src + |> compileWithFlags realsig optimize + |> compileAndRun + |> shouldSucceed + |> verifyPEFileWithSystemDlls + |> shouldSucceed + result |> verifyILPresent present + result |> verifyILNotPresent absent diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_StateMachineGenerators.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_StateMachineGenerators.fs new file mode 100644 index 00000000000..153ffc1bbce --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/Regression_TLR_StateMachineGenerators.fs @@ -0,0 +1,77 @@ +/// Purpose: cover sequence / task / async state-machine generators declared as members of +/// GENERIC classes. Each generator compiles to a nested state-machine type; class tyvars +/// must be threaded into that generated type correctly and the state machine must stay +/// scoped inside its declaring class (never a module sibling), which is the same scope +/// rule the closures in Regression_RealsigAugmentationClosure.fs enforce. +/// +/// Existing coverage is limited to seq/task/async in NON-generic augmentation members +/// (#19955); generic-class members (body-declared AND generic methods) are the gap here. +/// +/// Variant policy: runtime = 4-way matrix; IL = realsig +/- under --optimize+ only (the +/// nested-state-machine token is an optimize+ codegen artifact). No IL baselines are +/// generated; the nested/absent fragment pairs below pin only the *scope*, which is the +/// invariant that matters. +namespace EmittedIL.RealInternalSignature + +open Xunit +open FSharp.Test +open FSharp.Test.Compiler + +module Regression_TLR_StateMachineGenerators = + + let private compileWithFlags realsig optimize source = + FSharp source + |> withRealInternalSignature realsig + |> asExe + |> withOptimization optimize + |> ignoreWarnings + + /// What: seq/task/async/seq-in-generic-method generators on a generic class run to the + /// correct values under every realsig/optimize combination. + /// Why: the generators capture the class tyvar 'T, so any tyvar or closure-scope slip in + /// the generated state-machine class produces either a type error or a runtime failure. + /// Breaks if: the state machine is emitted in a scope that cannot reach the enclosing + /// type's members/tyvars, or the tyvar is dropped from the generated type entirely. + + /// What: IL scope lock -- the generated seq/task/async state machines must be nested + /// inside the generic class (`Holder`1/Seq@`, `/*/Gen@`, `/*/Task@`, `/*/Async@`) and + /// never appear as module-class siblings. + /// Why: a sibling state machine that closes over the class tyvar or a type-private + /// member is unverifiable or throws at first use (the #19933 failure mode); nesting is + /// the contract. + /// Breaks if: the state machine is hoisted out of the class. Manual .il.bsl capture for + /// both realsig values belongs beside this file once the homing change lands. + [] + let ``Seq, task and async generators on a generic class run`` (realsig: bool, optimize: bool) = + let src = + """ +module Sample +open System.Threading.Tasks +type Holder<'T>(initial: 'T) = + member _.Seq() = seq { yield initial } + member _.Gen<'U>(u: 'U) = seq { yield u } + member _.Task() : Task = task { return 42 } + member _.Async() = async { return initial } + +[] +let main _ = + let h = Holder(7) + let s = h.Seq() |> Seq.head + let g = h.Gen("x") |> Seq.head + let t = h.Task().Result + let a = h.Async() |> Async.RunSynchronously + if s = 7 && g = "x" && t = 42 && a = 7 then 0 else 1 +""" + let (present, absent) = + match realsig, optimize with + | false, _ -> [ "Sample/Seq@"; "Sample/Gen@"; "Sample/Task@"; "Sample/Async@" ], [ "Holder`1/Seq@"; "Holder`1/Gen@"; "Holder`1/Task@"; "Holder`1/Async@" ] + | true, _ -> [ "Holder`1/Seq@"; "Holder`1/Gen@"; "Holder`1/Task@"; "Holder`1/Async@" ], [ "Sample/Seq@"; "Sample/Gen@"; "Sample/Task@"; "Sample/Async@" ] + + let result = + src + |> compileWithFlags realsig optimize + |> compileAndRun + |> shouldSucceed + |> verifyPEFileWithSystemDlls + result |> verifyILPresent present + result |> verifyILNotPresent absent diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/BigTuples.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/BigTuples.fs new file mode 100644 index 00000000000..d6f2077cc44 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/BigTuples.fs @@ -0,0 +1,3 @@ +namespace Equality + +type BigGenericTuple<'a> = BigGenericTuple of int * 'a * byte * int * 'a * byte diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/ClassTypeInitialization.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/ClassTypeInitialization.fs index 33ebc04f71d..2f43f4c4c6a 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/ClassTypeInitialization.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/ClassTypeInitialization.fs @@ -554,21 +554,100 @@ printfn "%A" (MyClass.result()) [] // Regular Optimize [] // Regular NoOptimize [] - let ``nested generic closure`` (realSig, optimize) = + let ``nested generic closure`` (realsig, optimize) = let path = __SOURCE_DIRECTORY__ ++ "nested_generic_closure.fs" let source = File.ReadAllText (path) - FSharp source - |> withName "NestedGenericClosure" - |> asExe - |> withRealInternalSignature realSig - |> withOptimization optimize - |> compileAndRun - |> shouldSucceed - |> verifyPEFileWithSystemDlls - |> withOutputContainsAllInOrderWithWildcards [ - "All Classes and Methods in*NestedGenericClosure.exe Verified." - ] + let result = + FSharp source + |> withName "NestedGenericClosure" + |> asExe + |> withRealInternalSignature realsig + |> withOptimization optimize + |> compileAndRun + |> verifyPEFileWithSystemDlls + |> withOutputContainsAllInOrderWithWildcards [ + "All Classes and Methods in*NestedGenericClosure.exe Verified." + ] + |> shouldSucceed + + // Verify rehoming operates correctly + // The text is sufficient to verify the correct homing it is not the entire line of the il. It is sufficient to ensure rehoming occured. + let (present, absent) = + match realsig, optimize with + | false, false -> + [ + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeInner@303::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeOuter@309::.ctor" + "instance void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/iter@273::.ctor" + ], + [ + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/takeInner@303::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/takeOuter@309::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/iter@273::.ctor" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeInner@301" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeOuter@308" + "call void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::iter@272" + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeInner@301(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeOuter@308(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::iter@272(class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>)" + ] + | false, true -> + [ + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeInner@301" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeOuter@308" + "call void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::iter@272" + ], + [ + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeInner@303::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeOuter@309::.ctor" + "instance void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/iter@273::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/takeInner@303::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/takeOuter@309::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/iter@273::.ctor" + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeInner@301(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeOuter@308(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::iter@272(class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>)" + ] + | true, false -> + [ + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/takeInner@303::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/takeOuter@309::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2/iter@273::.ctor" + ], + [ + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeInner@303::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeOuter@309::.ctor" + "instance void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/iter@273::.ctor" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeInner@301" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeOuter@308" + "call void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::iter@272" + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeInner@301(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeOuter@308(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::iter@272(class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>)" + ] + | true, true -> + [ + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeInner@301(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call bool class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::takeOuter@308(class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2," + "call void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/ConcatEnumerator`2::iter@272(class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>)" + ], + [ + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeInner@303::.ctor" + "instance void class Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/takeOuter@309::.ctor" + "instance void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers/iter@273::.ctor" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeInner@301" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeOuter@308" + "call void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::iter@272" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeInner@301" + "call bool Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::takeOuter@308" + "call void Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers::iter@272" + ] + result |> verifyILPresent present + result |> verifyILNotPresent absent + + // Verify IL conforms to clr rules + result [] // RealSig Optimize [] // RealSig NoOptimize @@ -792,12 +871,10 @@ type internal AgedLookup<'Token, 'Key, 'Value when 'Value: not struct>(keepStron [] // Regular NoOptimize [] let ``BigTuples`` (realSig, optimize) = + let path = __SOURCE_DIRECTORY__ ++ "BigTuples.fs" + let source = File.ReadAllText (path) - FSharp """ -namespace Equality - -type BigGenericTuple<'a> = BigGenericTuple of int * 'a * byte * int * 'a * byte - """ + FSharp source |> withName "BigTuples" |> asLibrary |> withRealInternalSignature realSig diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/more_generic_closures.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/more_generic_closures.fs new file mode 100644 index 00000000000..86b88fc0eaf --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/more_generic_closures.fs @@ -0,0 +1,58 @@ +namespace more_generic_closures + +open System.Collections +open System.Collections.Generic + +type ConcatEnumerator<'T,'U when 'U :> seq<'T>>(sources : seq<'U>) as x = + + [] + val mutable internal outerEnum : IEnumerator<'U> + + [] + val mutable internal innerEnum : IEnumerator<'T> + + [] + val mutable public CurrentElement : 'T + + do + x.outerEnum <- sources.GetEnumerator() + x.innerEnum <- Seq.empty<'T>.GetEnumerator() + x.CurrentElement <- Unchecked.defaultof<'T> + + member x.MoveNext() : bool = + let rec takeInner () : bool = + if x.innerEnum.MoveNext() then + x.CurrentElement <- x.innerEnum.Current + true + else + takeOuter () + + and takeOuter () : bool = + if x.outerEnum.MoveNext() then + let ie : 'U = x.outerEnum.Current + x.innerEnum <- ie.GetEnumerator() + takeInner () + else + false + + takeInner () + +module Run = + let test() = + let data : seq> = + seq { + yield seq { 1; 2 } + yield seq { } + yield seq { 3 } + } + + let c = ConcatEnumerator>(data) + let r1 = c.MoveNext() + let r2 = c.MoveNext() + let r3 = c.MoveNext() + + printfn "MoveNext1 = %A, CurrentElement = %A" r1 c.CurrentElement + printfn "MoveNext2 = %A, CurrentElement = %A" r2 c.CurrentElement + printfn "MoveNext3 = %A, CurrentElement = %A" r3 c.CurrentElement + + test() diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/nested_generic_closure.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/nested_generic_closure.fs index 19e9fcaa665..737e85713f8 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/nested_generic_closure.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/RealInternalSignature/nested_generic_closure.fs @@ -346,4 +346,5 @@ namespace Microsoft.FSharp.Core.CompilerServices if enumerator.MoveNext() then printfn $"{enumerator.Current}" loop () + loop () diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 2b473df5e1b..a6c651fdb4c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -288,6 +288,11 @@ + + + + +