@@ -2781,20 +2781,30 @@ namespace {
2781
2781
2782
2782
private:
2783
2783
// / Produce a builder call to the given named function with the given arguments.
2784
- CallExpr *buildCallIfWanted (Identifier fnName, ArrayRef<Expr *> args) {
2784
+ CallExpr *buildCallIfWanted (SourceLoc loc,
2785
+ Identifier fnName, ArrayRef<Expr *> args,
2786
+ ArrayRef<Identifier> argLabels = {}) {
2785
2787
if (!wantExpr)
2786
2788
return nullptr ;
2787
2789
2788
- auto typeExpr = new (ctx) TypeExpr (TypeLoc ());
2790
+ // FIXME: Setting a TypeLoc on this expression is necessary in order
2791
+ // to get diagnostics if something about this builder call fails,
2792
+ // e.g. if there isn't a matching overload for `buildBlock`.
2793
+ // But we can only do this if there isn't a type variable in the type.
2794
+ TypeLoc typeLoc (nullptr ,
2795
+ builderType->hasTypeVariable () ? Type () : builderType);
2796
+
2797
+ auto typeExpr = new (ctx) TypeExpr (typeLoc);
2789
2798
cs.setType (typeExpr, builderType);
2790
2799
typeExpr->setImplicit ();
2791
2800
auto memberRef = new (ctx) UnresolvedDotExpr (
2792
- typeExpr, SourceLoc () , fnName, DeclNameLoc (), /* implicit=*/ true );
2793
- return CallExpr::createImplicit (ctx, memberRef, args, { } );
2801
+ typeExpr, loc , fnName, DeclNameLoc (), /* implicit=*/ true );
2802
+ return CallExpr::createImplicit (ctx, memberRef, args, argLabels );
2794
2803
}
2795
2804
2796
2805
// / Check whether the builder supports the given operation.
2797
- bool builderSupports (Identifier fnName) {
2806
+ bool builderSupports (Identifier fnName,
2807
+ ArrayRef<Identifier> argLabels = {}) {
2798
2808
auto known = supportedOps.find (fnName);
2799
2809
if (known != supportedOps.end ()) {
2800
2810
return known->second ;
@@ -2803,10 +2813,21 @@ namespace {
2803
2813
bool found = false ;
2804
2814
for (auto decl : builder->lookupDirect (fnName)) {
2805
2815
if (auto func = dyn_cast<FuncDecl>(decl)) {
2806
- if (func->isStatic ()) {
2807
- found = true ;
2808
- break ;
2816
+ // Function must be static.
2817
+ if (!func->isStatic ())
2818
+ continue ;
2819
+
2820
+ // Function must have the right argument labels, if provided.
2821
+ if (!argLabels.empty ()) {
2822
+ auto funcLabels = func->getFullName ().getArgumentNames ();
2823
+ if (argLabels.size () > funcLabels.size () ||
2824
+ funcLabels.slice (0 , argLabels.size ()) != argLabels)
2825
+ continue ;
2809
2826
}
2827
+
2828
+ // Okay, it's a good-enough match.
2829
+ found = true ;
2830
+ break ;
2810
2831
}
2811
2832
}
2812
2833
@@ -2850,7 +2871,8 @@ namespace {
2850
2871
}
2851
2872
2852
2873
// Call Builder.buildBlock(... args ...)
2853
- return buildCallIfWanted (ctx.Id_buildBlock , expressions);
2874
+ return buildCallIfWanted (braceStmt->getStartLoc (),
2875
+ ctx.Id_buildBlock , expressions);
2854
2876
}
2855
2877
2856
2878
Expr *visitReturnStmt (ReturnStmt *returnStmt) {
@@ -2868,7 +2890,7 @@ namespace {
2868
2890
if (!arg)
2869
2891
return nullptr ;
2870
2892
2871
- return buildCallIfWanted (ctx.Id_buildDo , arg);
2893
+ return buildCallIfWanted (doStmt-> getStartLoc (), ctx.Id_buildDo , arg);
2872
2894
}
2873
2895
2874
2896
CONTROL_FLOW_STMT (Yield)
@@ -2881,42 +2903,229 @@ namespace {
2881
2903
return condition.front ().getBooleanOrNull ();
2882
2904
}
2883
2905
2906
+ static bool isBuildableIfChainRecursive (IfStmt *ifStmt,
2907
+ unsigned &numPayloads,
2908
+ bool &isOptional) {
2909
+ // The conditional must be trivial.
2910
+ if (!getTrivialBooleanCondition (ifStmt->getCond ()))
2911
+ return false ;
2912
+
2913
+ // The 'then' clause contributes a payload.
2914
+ numPayloads++;
2915
+
2916
+ // If there's an 'else' clause, it contributes payloads:
2917
+ if (auto elseStmt = ifStmt->getElseStmt ()) {
2918
+ // If it's 'else if', it contributes payloads recursively.
2919
+ if (auto elseIfStmt = dyn_cast<IfStmt>(elseStmt)) {
2920
+ return isBuildableIfChainRecursive (elseIfStmt, numPayloads,
2921
+ isOptional);
2922
+ // Otherwise it's just the one.
2923
+ } else {
2924
+ numPayloads++;
2925
+ }
2926
+
2927
+ // If not, the chain result is at least optional.
2928
+ } else {
2929
+ isOptional = true ;
2930
+ }
2931
+
2932
+ return true ;
2933
+ }
2934
+
2935
+ bool isBuildableIfChain (IfStmt *ifStmt, unsigned &numPayloads,
2936
+ bool &isOptional) {
2937
+ if (!isBuildableIfChainRecursive (ifStmt, numPayloads, isOptional))
2938
+ return false ;
2939
+
2940
+ // If there's a missing 'else', we need 'buildIf' to exist.
2941
+ if (isOptional && !builderSupports (ctx.Id_buildIf ))
2942
+ return false ;
2943
+
2944
+ // If there are multiple clauses, we need 'buildEither(first:)' and
2945
+ // 'buildEither(second:)' to both exist.
2946
+ if (numPayloads > 1 ) {
2947
+ if (!builderSupports (ctx.Id_buildEither , {ctx.Id_first }) ||
2948
+ !builderSupports (ctx.Id_buildEither , {ctx.Id_second }))
2949
+ return false ;
2950
+ }
2951
+
2952
+ return true ;
2953
+ }
2954
+
2884
2955
Expr *visitIfStmt (IfStmt *ifStmt) {
2885
- if (!builderSupports (ctx.Id_buildIf ) ||
2886
- ifStmt->getElseStmt () ||
2887
- !getTrivialBooleanCondition (ifStmt->getCond ())) {
2956
+ // Check whether the chain is buildable and whether it terminates
2957
+ // without an `else`.
2958
+ bool isOptional = false ;
2959
+ unsigned numPayloads = 0 ;
2960
+ if (!isBuildableIfChain (ifStmt, numPayloads, isOptional)) {
2888
2961
if (!unhandledNode)
2889
2962
unhandledNode = ifStmt;
2890
2963
return nullptr ;
2891
2964
}
2892
2965
2893
- auto thenArg = visit (ifStmt->getThenStmt ());
2894
- if (!thenArg)
2966
+ // Attempt to build the chain, propagating short-circuits, which
2967
+ // might arise either do to error or not wanting an expression.
2968
+ auto chainExpr =
2969
+ buildIfChainRecursive (ifStmt, 0 , numPayloads, isOptional);
2970
+ if (!chainExpr)
2895
2971
return nullptr ;
2972
+ assert (wantExpr);
2896
2973
2897
- if (!wantExpr)
2974
+ // The operand should have optional type if we had optional results,
2975
+ // so we just need to call `buildIf` now, since we're at the top level.
2976
+ if (isOptional) {
2977
+ chainExpr = buildCallIfWanted (ifStmt->getStartLoc (),
2978
+ ctx.Id_buildIf , chainExpr);
2979
+ }
2980
+
2981
+ return chainExpr;
2982
+ }
2983
+
2984
+ // / Recursively build an if-chain: build an expression which will have
2985
+ // / a value of the chain result type before any call to `buildIf`.
2986
+ // / The expression will perform any necessary calls to `buildEither`,
2987
+ // / and the result will have optional type if `isOptional` is true.
2988
+ Expr *buildIfChainRecursive (IfStmt *ifStmt, unsigned payloadIndex,
2989
+ unsigned numPayloads, bool isOptional) {
2990
+ assert (payloadIndex < numPayloads);
2991
+ // Make sure we recursively visit both sides even if we're not
2992
+ // building expressions.
2993
+
2994
+ // Build the then clause. This will have the corresponding payload
2995
+ // type (i.e. not wrapped in any way).
2996
+ Expr *thenArg = visit (ifStmt->getThenStmt ());
2997
+
2998
+ // Build the else clause, if present. If this is from an else-if,
2999
+ // this will be fully wrapped; otherwise it will have the corresponding
3000
+ // payload type (at index `payloadIndex + 1`).
3001
+ assert (ifStmt->getElseStmt () || isOptional);
3002
+ bool isElseIf = false ;
3003
+ Optional<Expr *> elseChain;
3004
+ if (auto elseStmt = ifStmt->getElseStmt ()) {
3005
+ if (auto elseIfStmt = dyn_cast<IfStmt>(elseStmt)) {
3006
+ isElseIf = true ;
3007
+ elseChain = buildIfChainRecursive (elseIfStmt, payloadIndex + 1 ,
3008
+ numPayloads, isOptional);
3009
+ } else {
3010
+ elseChain = visit (elseStmt);
3011
+ }
3012
+ }
3013
+
3014
+ // Short-circuit if appropriate.
3015
+ if (!wantExpr || !thenArg || (elseChain && !*elseChain))
2898
3016
return nullptr ;
2899
3017
3018
+ // Okay, build the conditional expression.
3019
+
3020
+ // Prepare the `then` operand by wrapping it to produce a chain result.
3021
+ SourceLoc thenLoc = ifStmt->getThenStmt ()->getStartLoc ();
3022
+ Expr *thenExpr = buildWrappedChainPayload (thenArg, payloadIndex,
3023
+ numPayloads, isOptional);
3024
+
3025
+ // Prepare the `else operand:
3026
+ Expr *elseExpr;
3027
+ SourceLoc elseLoc;
3028
+
3029
+ // - If there's no `else` clause, use `Optional.none`.
3030
+ if (!elseChain) {
3031
+ assert (isOptional);
3032
+ elseLoc = ifStmt->getEndLoc ();
3033
+ elseExpr = buildNoneExpr (elseLoc);
3034
+
3035
+ // - If there's an `else if`, the chain expression from that
3036
+ // should already be producing a chain result.
3037
+ } else if (isElseIf) {
3038
+ elseExpr = *elseChain;
3039
+ elseLoc = ifStmt->getElseLoc ();
3040
+
3041
+ // - Otherwise, wrap it to produce a chain result.
3042
+ } else {
3043
+ elseLoc = ifStmt->getElseLoc ();
3044
+ elseExpr = buildWrappedChainPayload (*elseChain,
3045
+ payloadIndex + 1 , numPayloads,
3046
+ isOptional);
3047
+ }
3048
+
3049
+ Expr *condition = getTrivialBooleanCondition (ifStmt->getCond ());
3050
+ assert (condition && " checked by isBuildableIfChain" );
3051
+
3052
+ auto ifExpr = new (ctx) IfExpr (condition, thenLoc, thenExpr,
3053
+ elseLoc, elseExpr);
3054
+ ifExpr->setImplicit ();
3055
+ return ifExpr;
3056
+ }
3057
+
3058
+ // / Wrap a payload value in an expression which will produce a chain
3059
+ // / result (without `buildIf`).
3060
+ Expr *buildWrappedChainPayload (Expr *operand, unsigned payloadIndex,
3061
+ unsigned numPayloads, bool isOptional) {
3062
+ assert (payloadIndex < numPayloads);
3063
+
3064
+ // Inject into the appropriate chain position.
3065
+ //
3066
+ // We produce a (left-biased) balanced binary tree of Eithers in order
3067
+ // to prevent requiring a linear number of injections in the worst case.
3068
+ // That is, if we have 13 clauses, we want to produce:
3069
+ //
3070
+ // /------------------Either------------\
3071
+ // /-------Either-------\ /--Either--\
3072
+ // /--Either--\ /--Either--\ /--Either--\ \
3073
+ // /-E-\ /-E-\ /-E-\ /-E-\ /-E-\ /-E-\ \
3074
+ // 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100
3075
+ //
3076
+ // Note that a prefix of length D of the payload index acts as a path
3077
+ // through the tree to the node at depth D. On the rightmost path
3078
+ // through the tree (when this prefix is equal to the corresponding
3079
+ // prefix of the maximum payload index), the bits of the index mark
3080
+ // where Eithers are required.
3081
+ //
3082
+ // Since we naturally want to build from the innermost Either out, and
3083
+ // therefore work with progressively shorter prefixes, we can do it all
3084
+ // with right-shifts.
3085
+ for (auto path = payloadIndex, maxPath = numPayloads - 1 ;
3086
+ maxPath != 0 ; path >>= 1 , maxPath >>= 1 ) {
3087
+ // Skip making Eithers on the rightmost path where they aren't required.
3088
+ // This isn't just an optimization: adding spurious Eithers could
3089
+ // leave us with unresolvable type variables if `buildEither` has
3090
+ // a signature like:
3091
+ // static func buildEither<T,U>(first value: T) -> Either<T,U>
3092
+ // which relies on unification to work.
3093
+ if (path == maxPath && !(maxPath & 1 )) continue ;
3094
+
3095
+ bool isSecond = (path & 1 );
3096
+ operand = buildCallIfWanted (operand->getStartLoc (),
3097
+ ctx.Id_buildEither , operand,
3098
+ {isSecond ? ctx.Id_second : ctx.Id_first });
3099
+ }
3100
+
3101
+ // Inject into Optional if required. We'll be adding the call to
3102
+ // `buildIf` after all the recursive calls are complete.
3103
+ if (isOptional) {
3104
+ operand = buildSomeExpr (operand);
3105
+ }
3106
+
3107
+ return operand;
3108
+ }
3109
+
3110
+ Expr *buildSomeExpr (Expr *arg) {
2900
3111
auto optionalDecl = ctx.getOptionalDecl ();
2901
3112
auto optionalType = optionalDecl->getDeclaredType ();
2902
3113
2903
3114
auto optionalTypeExpr = TypeExpr::createImplicit (optionalType, ctx);
2904
3115
auto someRef = new (ctx) UnresolvedDotExpr (
2905
3116
optionalTypeExpr, SourceLoc (), ctx.getIdentifier (" some" ),
2906
3117
DeclNameLoc (), /* implicit=*/ true );
2907
- auto someCall = CallExpr::createImplicit (ctx, someRef, thenArg, { });
3118
+ return CallExpr::createImplicit (ctx, someRef, arg, { });
3119
+ }
2908
3120
2909
- optionalTypeExpr = TypeExpr::createImplicit (optionalType, ctx);
2910
- auto noneRef = new (ctx) UnresolvedDotExpr (
2911
- optionalTypeExpr, ifStmt->getEndLoc (), ctx.getIdentifier (" none" ),
2912
- DeclNameLoc (ifStmt->getEndLoc ()), /* implicit=*/ true );
3121
+ Expr *buildNoneExpr (SourceLoc endLoc) {
3122
+ auto optionalDecl = ctx.getOptionalDecl ();
3123
+ auto optionalType = optionalDecl->getDeclaredType ();
2913
3124
2914
- auto ifExpr = new (ctx) IfExpr (
2915
- getTrivialBooleanCondition (ifStmt->getCond ()),
2916
- SourceLoc (), someCall,
2917
- SourceLoc (), noneRef);
2918
- ifExpr->setImplicit ();
2919
- return buildCallIfWanted (ctx.Id_buildIf , ifExpr);
3125
+ auto optionalTypeExpr = TypeExpr::createImplicit (optionalType, ctx);
3126
+ return new (ctx) UnresolvedDotExpr (
3127
+ optionalTypeExpr, endLoc, ctx.getIdentifier (" none" ),
3128
+ DeclNameLoc (endLoc), /* implicit=*/ true );
2920
3129
}
2921
3130
2922
3131
CONTROL_FLOW_STMT (Guard)
0 commit comments