@@ -11279,17 +11279,6 @@ and GenSprintfPrintingMethod cenv eenv methName ilThisTy m =
1127911279 | _ -> ()
1128011280 ]
1128111281
11282- /// Generate the 'ToString' method for a union type. Normally this calls 'sprintf "%+A"' (see
11283- /// GenSprintfPrintingMethod). Under reflection-free code generation 'sprintf' is unavailable, so instead emit a
11284- /// match over the cases that builds "CaseName(f0, f1, ...)" using the 'string' operator on each field.
11285- /// Format one field value the same way option/list do (LanguagePrimitives.anyToStringShowingNull):
11286- /// render null as "null", otherwise via the 'string' operator.
11287- and GenFieldToString (cenv: cenv, m: range, fe: Expr) =
11288- let g = cenv.g
11289- let fieldTy = tyOfExpr g fe
11290- let v, ve = mkCompGenLocal m "field" fieldTy
11291- mkCompGenLet m v fe (mkNonNullCond g m g.string_ty (mkCallBox g m fieldTy ve) (mkCallStringOperator g m fieldTy ve) (mkString g m "null"))
11292-
1129311282/// Emit a [<CompilerGenerated>] virtual ToString override whose body is the given string-typed expression.
1129411283/// 'thisv' is the 'this' value (stored at arg 0) referenced by bodyExpr.
1129511284and EmitToStringMethodDef (cenv: cenv, mgbuf: AssemblyBuilder, eenv: IlxGenEnv, thisv: Val, bodyExpr: Expr) =
@@ -11308,84 +11297,18 @@ and EmitToStringMethodDef (cenv: cenv, mgbuf: AssemblyBuilder, eenv: IlxGenEnv,
1130811297
1130911298 [ mdef.With(customAttrs = mkILCustomAttrs [ g.CompilerGeneratedAttribute ]) ]
1131011299
11311- /// Build the 'this' local for a generated ToString (a byref for struct types) and the type instantiation.
11312- and GenToStringThis (cenv: cenv, tcref: TyconRef, m: range) =
11313- let g = cenv.g
11314- let tinst, ty = generalizeTyconRef g tcref
11315- let thisv, thise = mkCompGenLocal m "this" (if isStructTy g ty then mkByrefTy g ty else ty)
11316- tinst, thisv, thise
11317-
11318- and GenUnionToStringMethod (cenv: cenv, mgbuf: AssemblyBuilder, eenv: IlxGenEnv, ilThisTy: ILType, tcref: TyconRef, m: range) =
11319- let g = cenv.g
11320-
11321- if not g.useReflectionFreeCodeGen then
11322- GenSprintfPrintingMethod cenv eenv "ToString" ilThisTy m
11323- else
11324- let tinst, thisv, thise = GenToStringThis (cenv, tcref, m)
11325-
11326- let mbuilder = MatchBuilder(DebugPointAtBinding.NoneAtInvisible, m)
11327-
11328- let mkResult (ucase: UnionCase) =
11329- let cref = tcref.MakeNestedUnionCaseRef ucase
11330- let rfields = ucase.RecdFields
11331-
11332- if isNil rfields then
11333- mkString g m ucase.DisplayName
11334- else
11335- // provene is an expression proven to be of this case (the value itself for struct unions,
11336- // otherwise a 'UnionCaseProof'), from which fields can be read.
11337- let mkBody (provene: Expr) =
11338- let fieldStrs =
11339- rfields
11340- |> List.mapi (fun j _ -> GenFieldToString (cenv, m, mkUnionCaseFieldGetProvenViaExprAddr (provene, cref, tinst, j, m)))
11341-
11342- let sep = mkString g m ", "
11343-
11344- let fieldsWithSeps =
11345- fieldStrs |> List.mapi (fun i fe -> if i = 0 then [ fe ] else [ sep; fe ]) |> List.concat
11346-
11347- let parts = mkString g m (ucase.DisplayName + "(") :: fieldsWithSeps @ [ mkString g m ")" ]
11348- mkStringConcat (g, m, parts)
11349-
11350- if cref.Tycon.IsStructOrEnumTycon then
11351- mkBody thise
11352- else
11353- let ucv, ucve = mkCompGenLocal m "thisCast" (mkProvenUnionCaseTy cref tinst)
11354- mkCompGenLet m ucv (mkUnionCaseProof (thise, cref, tinst, m)) (mkBody ucve)
11355-
11356- let cases =
11357- tcref.UnionCasesAsList
11358- |> List.map (fun ucase ->
11359- let cref = tcref.MakeNestedUnionCaseRef ucase
11360- mkCase (DecisionTreeTest.UnionCase(cref, tinst), mbuilder.AddResultTarget(mkResult ucase)))
11361-
11362- let dtree = TDSwitch(thise, cases, None, m)
11363- let matchExpr = mbuilder.Close(dtree, m, g.string_ty)
11364-
11365- EmitToStringMethodDef (cenv, mgbuf, eenv, thisv, matchExpr)
11366-
11367- /// Generate a record's ToString as a single line "{ F1 = v1; F2 = v2 }" (no line breaks, unlike "%+A"),
11368- /// fields formatted like union fields. openBrace/closeBrace are "{ "/" }" for records and "{| "/" |}" for
11369- /// anonymous records. Under non-reflection-free codegen, falls back to sprintf "%+A".
11300+ /// Generate an anonymous record's ToString as a single line "{| F1 = v1; F2 = v2 |}". Nominal records and
11301+ /// unions get their reflection-free ToString from the type-augmentation phase instead (so the 'string'
11302+ /// operator calls are optimized), but anonymous record types are synthesized too late for that, so they are
11303+ /// generated here. Under non-reflection-free codegen, falls back to sprintf "%+A".
1137011304and GenRecordToStringMethod (cenv: cenv, mgbuf: AssemblyBuilder, eenv: IlxGenEnv, ilThisTy: ILType, tcref: TyconRef, m: range, openBrace: string, closeBrace: string) =
1137111305 let g = cenv.g
1137211306
1137311307 if not g.useReflectionFreeCodeGen then
1137411308 GenSprintfPrintingMethod cenv eenv "ToString" ilThisTy m
1137511309 else
11376- let tinst, thisv, thise = GenToStringThis (cenv, tcref, m)
11377-
11378- let fieldParts =
11379- tcref.AllInstanceFieldsAsList
11380- |> List.mapi (fun i fspec ->
11381- let fref = tcref.MakeNestedRecdFieldRef fspec
11382- let value = GenFieldToString (cenv, m, mkRecdFieldGetViaExprAddr (thise, fref, tinst, m))
11383- let nameEq = mkString g m (fspec.DisplayName + " = ")
11384- if i = 0 then [ nameEq; value ] else [ mkString g m "; "; nameEq; value ])
11385- |> List.concat
11386-
11387- let parts = mkString g m openBrace :: fieldParts @ [ mkString g m closeBrace ]
11388- EmitToStringMethodDef (cenv, mgbuf, eenv, thisv, mkStringConcat (g, m, parts))
11310+ let thisv, body = AugmentTypeDefinitions.mkRecdToString (g, tcref, tcref.Deref, openBrace, closeBrace)
11311+ EmitToStringMethodDef (cenv, mgbuf, eenv, thisv, body)
1138911312
1139011313and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) : ILTypeRef option =
1139111314 let g = cenv.g
@@ -11970,8 +11893,10 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) : ILTypeRef option
1197011893 then
1197111894 yield mkILSimpleStorageCtor (Some g.ilg.typ_Object.TypeSpec, ilThisTy, [], [], reprAccess, None, eenv.imports)
1197211895
11973- if not (tycon.HasMember g "ToString" []) then
11974- yield! GenRecordToStringMethod(cenv, mgbuf, eenvinner, ilThisTy, tcref, m, "{ ", " }")
11896+ // Reflection-free nominal records get their ToString from the type-augmentation phase; here we
11897+ // only emit the sprintf "%+A" ToString for the non-reflection-free case.
11898+ if not g.useReflectionFreeCodeGen && not (tycon.HasMember g "ToString" []) then
11899+ yield! GenSprintfPrintingMethod cenv eenvinner "ToString" ilThisTy m
1197511900
1197611901 | TFSharpTyconRepr r when tycon.IsFSharpDelegateTycon ->
1197711902
@@ -11994,8 +11919,10 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) : ILTypeRef option
1199411919 yield! mkILDelegateMethods reprAccess g.ilg (g.iltyp_AsyncCallback, g.iltyp_IAsyncResult) (parameters, ret)
1199511920 | _ -> ()
1199611921
11997- | TFSharpTyconRepr { fsobjmodel_kind = TFSharpUnion } when not (tycon.HasMember g "ToString" []) ->
11998- yield! GenUnionToStringMethod(cenv, mgbuf, eenvinner, ilThisTy, tcref, m)
11922+ // Reflection-free nominal unions get their ToString from the type-augmentation phase; here we
11923+ // only emit the sprintf "%+A" ToString for the non-reflection-free case.
11924+ | TFSharpTyconRepr { fsobjmodel_kind = TFSharpUnion } when not g.useReflectionFreeCodeGen && not (tycon.HasMember g "ToString" []) ->
11925+ yield! GenSprintfPrintingMethod cenv eenvinner "ToString" ilThisTy m
1199911926 | _ -> ()
1200011927 ]
1200111928
0 commit comments