@@ -56,6 +56,8 @@ enum class WellKnownFunction {
5656 StringEquals,
5757 // String.percentEscapedString.getter
5858 StringEscapePercent,
59+ // BinaryInteger.description.getter
60+ BinaryIntegerDescription,
5961 // _assertionFailure(_: StaticString, _: StaticString, file: StaticString,...)
6062 AssertionFailure,
6163 // A function taking one argument that prints the symbolic value of the
@@ -83,6 +85,8 @@ static llvm::Optional<WellKnownFunction> classifyFunction(SILFunction *fn) {
8385 return WellKnownFunction::StringEquals;
8486 if (fn->hasSemanticsAttr (semantics::STRING_ESCAPE_PERCENT_GET))
8587 return WellKnownFunction::StringEscapePercent;
88+ if (fn->hasSemanticsAttr (semantics::BINARY_INTEGER_DESCRIPTION))
89+ return WellKnownFunction::BinaryIntegerDescription;
8690 if (fn->hasSemanticsAttrThatStartsWith (" programtermination_point" ))
8791 return WellKnownFunction::AssertionFailure;
8892 // A call to a function with the following semantics annotation will be
@@ -780,6 +784,13 @@ extractStaticStringValue(SymbolicValue staticString) {
780784 return staticStringProps[0 ].getStringValue ();
781785}
782786
787+ static Optional<StringRef>
788+ extractStringOrStaticStringValue (SymbolicValue stringValue) {
789+ if (stringValue.getKind () == SymbolicValue::String)
790+ return stringValue.getStringValue ();
791+ return extractStaticStringValue (stringValue);
792+ }
793+
783794// / If the specified type is a Swift.Array of some element type, then return the
784795// / element type. Otherwise, return a null Type.
785796static Type getArrayElementType (Type ty) {
@@ -789,6 +800,28 @@ static Type getArrayElementType(Type ty) {
789800 return Type ();
790801}
791802
803+ // / Check if the given type \p ty is a stdlib integer type and if so return
804+ // / whether the type is signed. Returns \c None if \p ty is not a stdlib integer
805+ // / type, \c true if it is a signed integer type and \c false if it is an
806+ // / unsigned integer type.
807+ static Optional<bool > getSignIfStdlibIntegerType (Type ty) {
808+ StructDecl *decl = ty->getStructOrBoundGenericStruct ();
809+ if (!decl)
810+ return None;
811+ ASTContext &astCtx = ty->getASTContext ();
812+ if (decl == astCtx.getIntDecl () || decl == astCtx.getInt8Decl () ||
813+ decl == astCtx.getInt16Decl () || decl == astCtx.getInt32Decl () ||
814+ decl == astCtx.getInt64Decl ()) {
815+ return true ;
816+ }
817+ if (decl == astCtx.getUIntDecl () || decl == astCtx.getUInt8Decl () ||
818+ decl == astCtx.getUInt16Decl () || decl == astCtx.getUInt32Decl () ||
819+ decl == astCtx.getUInt64Decl ()) {
820+ return false ;
821+ }
822+ return None;
823+ }
824+
792825// / Given a call to a well known function, collect its arguments as constants,
793826// / fold it, and return None. If any of the arguments are not constants, marks
794827// / the call's results as Unknown, and return an Unknown with information about
@@ -803,8 +836,8 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply,
803836 for (unsigned i = 0 ; i < apply->getNumArguments (); i++) {
804837 SILValue argument = apply->getArgument (i);
805838 SymbolicValue argValue = getConstantValue (argument);
806- Optional<StringRef> stringOpt = extractStaticStringValue (argValue);
807-
839+ Optional<StringRef> stringOpt =
840+ extractStringOrStaticStringValue (argValue);
808841 // The first argument is a prefix that specifies the kind of failure
809842 // this is.
810843 if (i == 0 ) {
@@ -816,7 +849,6 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply,
816849 }
817850 continue ;
818851 }
819-
820852 if (stringOpt) {
821853 message += " : " ;
822854 message += stringOpt.getValue ();
@@ -1064,6 +1096,42 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply,
10641096 setValue (apply, resultVal);
10651097 return None;
10661098 }
1099+ case WellKnownFunction::BinaryIntegerDescription: {
1100+ // BinaryInteger.description.getter
1101+ assert (conventions.getNumDirectSILResults () == 1 &&
1102+ conventions.getNumIndirectSILResults () == 0 &&
1103+ conventions.getNumParameters () == 1 && apply->hasSubstitutions () &&
1104+ " unexpected BinaryInteger.description.getter signature" );
1105+ // Get the type of the argument and check if it is a signed or
1106+ // unsigned integer.
1107+ SILValue integerArgument = apply->getOperand (1 );
1108+ CanType argumentType = substituteGenericParamsAndSimpify (
1109+ integerArgument->getType ().getASTType ());
1110+ Optional<bool > isSignedIntegerType =
1111+ getSignIfStdlibIntegerType (argumentType);
1112+ if (!isSignedIntegerType.hasValue ()) {
1113+ return getUnknown (evaluator, (SILInstruction *)apply,
1114+ UnknownReason::InvalidOperandValue);
1115+ }
1116+ // Load the stdlib integer's value and convert it to a string.
1117+ SymbolicValue stdlibIntegerValue =
1118+ getConstAddrAndLoadResult (integerArgument);
1119+ if (!stdlibIntegerValue.isConstant ()) {
1120+ return stdlibIntegerValue;
1121+ }
1122+ SymbolicValue builtinIntegerValue =
1123+ stdlibIntegerValue.lookThroughSingleElementAggregates ();
1124+ assert (builtinIntegerValue.getKind () == SymbolicValue::Integer &&
1125+ " stdlib integer type must store only a builtin integer" );
1126+ APInt integer = builtinIntegerValue.getIntegerValue ();
1127+ SmallString<8 > integerString;
1128+ isSignedIntegerType.getValue () ? integer.toStringSigned (integerString)
1129+ : integer.toStringUnsigned (integerString);
1130+ SymbolicValue resultVal =
1131+ SymbolicValue::getString (integerString.str (), evaluator.getAllocator ());
1132+ setValue (apply, resultVal);
1133+ return None;
1134+ }
10671135 case WellKnownFunction::DebugPrint: {
10681136 assert (apply->getNumArguments () == 1 &&
10691137 " debug_print function must take exactly one argument" );
0 commit comments