Skip to content

Commit e90a68f

Browse files
committed
[polymorphic-builtins] Teach dataflow diagnostics how to emit an error if it sees an unspecialized polymorphic builtin.
This will ensure that if an expert user is using this feature and makes a mistake as a result of tweaking their code, they get an error. This will ensure they do not ship and look into why this is happening. This is not intended to be used by anyone except for expert stdlib users.
1 parent 4804444 commit e90a68f

File tree

8 files changed

+200
-13
lines changed

8 files changed

+200
-13
lines changed

include/swift/AST/Builtins.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,10 @@ class IntrinsicInfo {
131131

132132
/// Turn a string like "release" into the LLVM enum.
133133
llvm::AtomicOrdering decodeLLVMAtomicOrdering(StringRef O);
134-
134+
135+
/// Returns true if the builtin with ID \p ID has a defined static overload for
136+
/// the type \p Ty.
137+
bool canBuiltinBeOverloadedForType(BuiltinValueKind ID, Type Ty);
135138
}
136139

137140
#endif

include/swift/AST/DiagnosticsSIL.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,16 @@ ERROR(oslog_property_not_constant, none, "'OSLogInterpolation.%0' is not a "
522522
ERROR(global_string_pointer_on_non_constant, none, "globalStringTablePointer "
523523
"builtin must used only on string literals", ())
524524

525+
ERROR(polymorphic_builtin_passed_non_trivial_non_builtin_type, none, "Argument "
526+
"of type %0 can not be passed as an argument to a Polymorphic "
527+
"builtin. Polymorphic builtins can only be passed arguments that are "
528+
"trivial builtin typed", (Type))
529+
530+
ERROR(polymorphic_builtin_passed_type_without_static_overload, none, "Static"
531+
" overload %0 does not exist for polymorphic builtin '%1'. Static "
532+
"overload implied by passing argument of type %2",
533+
(Identifier, StringRef, Type))
534+
525535
#ifndef DIAG_NO_UNDEF
526536
# if defined(DIAG)
527537
# undef DIAG

include/swift/SIL/InstructionUtils.h

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,23 +149,37 @@ void findClosuresForFunctionValue(SILValue V,
149149
/// NOTE: If we perform this transformation, our builtin will no longer have any
150150
/// substitutions since we only substitute to concrete static overloads.
151151
struct PolymorphicBuiltinSpecializedOverloadInfo {
152+
const BuiltinInfo *builtinInfo;
152153
Identifier staticOverloadIdentifier;
153154
SmallVector<SILType, 8> argTypes;
154155
SILType resultType;
155-
bool hasOutParam = false;
156+
bool hasOutParam;
156157

157158
#ifndef NDEBUG
158159
private:
159-
bool isInitialized = false;
160-
#endif
160+
bool isInitialized;
161161

162162
public:
163-
PolymorphicBuiltinSpecializedOverloadInfo() = default;
163+
#endif
164164

165-
bool init(SILFunction *fn, BuiltinValueKind builtinKind,
166-
ArrayRef<SILType> oldOperandTypes, SILType oldResultType);
165+
PolymorphicBuiltinSpecializedOverloadInfo()
166+
: builtinInfo(nullptr), staticOverloadIdentifier(), argTypes(),
167+
resultType(), hasOutParam(false), isInitialized(false) {}
167168

169+
/// Returns true if we were able to map the polymorphic builtin to a static
170+
/// overload. False otherwise.
171+
///
172+
/// NOTE: This does not mean that the static overload actually exists.
168173
bool init(BuiltinInst *bi);
174+
175+
bool doesOverloadExist() const {
176+
CanBuiltinType builtinType = argTypes.front().getAs<BuiltinType>();
177+
return canBuiltinBeOverloadedForType(builtinInfo->ID, builtinType);
178+
}
179+
180+
private:
181+
bool init(SILFunction *fn, BuiltinValueKind builtinKind,
182+
ArrayRef<SILType> oldOperandTypes, SILType oldResultType);
169183
};
170184

171185
/// Given a polymorphic builtin \p bi, analyze its types and create a builtin

lib/AST/Builtins.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,13 @@ inline bool isBuiltinTypeOverloaded(Type T, OverloadedBuiltinKind OK) {
12561256
llvm_unreachable("bad overloaded builtin kind");
12571257
}
12581258

1259+
bool swift::canBuiltinBeOverloadedForType(BuiltinValueKind ID, Type Ty) {
1260+
if (ID == BuiltinValueKind::None)
1261+
return false;
1262+
1263+
return isBuiltinTypeOverloaded(Ty, OverloadedBuiltinKinds[unsigned(ID)]);
1264+
}
1265+
12591266
/// Table of string intrinsic names indexed by enum value.
12601267
static const char *const IntrinsicNameTable[] = {
12611268
"not_intrinsic",

lib/SIL/InstructionUtils.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,12 @@ bool PolymorphicBuiltinSpecializedOverloadInfo::init(
598598

599599
auto &ctx = fn->getASTContext();
600600
staticOverloadIdentifier = ctx.getIdentifier(staticOverloadName);
601+
602+
// Ok, we have our overload identifier. Grab the builtin info from the
603+
// cache. If we did not actually found a valid builtin value kind for our
604+
// overload, then we do not have a static overload for the passed in types, so
605+
// return false.
606+
builtinInfo = &fn->getModule().getBuiltinInfo(staticOverloadIdentifier);
601607
return true;
602608
}
603609

lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "swift/SILOptimizer/PassManager/Passes.h"
14-
#include "swift/SILOptimizer/PassManager/Transforms.h"
15-
#include "swift/SILOptimizer/Utils/ConstExpr.h"
1613
#include "swift/AST/ASTContext.h"
1714
#include "swift/AST/DiagnosticEngine.h"
18-
#include "swift/AST/DiagnosticsSema.h"
1915
#include "swift/AST/DiagnosticsSIL.h"
16+
#include "swift/AST/DiagnosticsSema.h"
2017
#include "swift/AST/Expr.h"
2118
#include "swift/AST/Stmt.h"
19+
#include "swift/SIL/InstructionUtils.h"
20+
#include "swift/SIL/SILConstants.h"
2221
#include "swift/SIL/SILFunction.h"
2322
#include "swift/SIL/SILInstruction.h"
2423
#include "swift/SIL/SILLocation.h"
2524
#include "swift/SIL/SILModule.h"
2625
#include "swift/SIL/SILVisitor.h"
27-
#include "swift/SIL/SILConstants.h"
26+
#include "swift/SILOptimizer/PassManager/Passes.h"
27+
#include "swift/SILOptimizer/PassManager/Transforms.h"
28+
#include "swift/SILOptimizer/Utils/ConstExpr.h"
2829

2930
using namespace swift;
3031

@@ -175,6 +176,67 @@ static void diagnosePoundAssert(const SILInstruction *I,
175176
}
176177
}
177178

179+
static void diagnoseUnspecializedPolymorphicBuiltins(SILInstruction *inst) {
180+
// We only validate if we are in a non-transparent function.
181+
if (inst->getFunction()->isTransparent())
182+
return;
183+
184+
auto *bi = dyn_cast<BuiltinInst>(inst);
185+
if (!bi)
186+
return;
187+
188+
auto kind = bi->getBuiltinKind();
189+
if (!kind)
190+
return;
191+
192+
if (!isPolymorphicBuiltin(*kind))
193+
return;
194+
195+
const auto &builtinInfo = bi->getBuiltinInfo();
196+
197+
// First that the parameters were acceptable so we can emit a nice error to
198+
// guide the user.
199+
for (SILValue value : bi->getOperandValues()) {
200+
SILType type = value->getType();
201+
SourceLoc loc;
202+
if (auto *inst = value->getDefiningInstruction()) {
203+
loc = inst->getLoc().getSourceLoc();
204+
} else {
205+
loc = bi->getLoc().getSourceLoc();
206+
}
207+
208+
if (!type.is<BuiltinType>() || !type.isTrivial(*bi->getFunction())) {
209+
diagnose(bi->getModule().getASTContext(), loc,
210+
diag::polymorphic_builtin_passed_non_trivial_non_builtin_type,
211+
type.getASTType());
212+
return;
213+
}
214+
}
215+
216+
// Ok, we have a valid type for a polymorphic builtin. Make sure we actually
217+
// have a static overload for this type.
218+
PolymorphicBuiltinSpecializedOverloadInfo overloadInfo;
219+
bool ableToMapToStaticOverload = overloadInfo.init(bi);
220+
(void)ableToMapToStaticOverload;
221+
assert(ableToMapToStaticOverload);
222+
if (!overloadInfo.doesOverloadExist()) {
223+
diagnose(bi->getModule().getASTContext(), bi->getLoc().getSourceLoc(),
224+
diag::polymorphic_builtin_passed_type_without_static_overload,
225+
overloadInfo.staticOverloadIdentifier,
226+
getBuiltinName(builtinInfo.ID),
227+
overloadInfo.argTypes.front().getASTType());
228+
return;
229+
}
230+
231+
// Otherwise, something happen that we did not understand. This can only
232+
// happen if we specialize the generic type in the builtin /after/ constant
233+
// propagation runs at -Onone but before dataflow diagnostics. This is an
234+
// error in implementation, so we assert.
235+
llvm_unreachable("Found generic builtin with known static overload that it "
236+
"could be transformed to. Did this builtin get its generic "
237+
"type specialized /after/ constant propagation?");
238+
}
239+
178240
namespace {
179241
class EmitDFDiagnostics : public SILFunctionTransform {
180242
~EmitDFDiagnostics() override {}
@@ -186,11 +248,13 @@ class EmitDFDiagnostics : public SILFunctionTransform {
186248
return;
187249

188250
SILModule &M = getFunction()->getModule();
189-
for (auto &BB : *getFunction())
251+
for (auto &BB : *getFunction()) {
190252
for (auto &I : BB) {
191253
diagnoseUnreachable(&I, M.getASTContext());
192254
diagnoseStaticReports(&I, M);
255+
diagnoseUnspecializedPolymorphicBuiltins(&I);
193256
}
257+
}
194258

195259
if (M.getASTContext().LangOpts.EnableExperimentalStaticAssert) {
196260
SymbolicValueBumpAllocator allocator;
@@ -202,6 +266,7 @@ class EmitDFDiagnostics : public SILFunctionTransform {
202266
}
203267
}
204268
};
269+
205270
} // end anonymous namespace
206271

207272

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-sil-opt -module-name Swift -dataflow-diagnostics -verify %s
2+
3+
sil_stage raw
4+
5+
import Builtin
6+
7+
struct MyInt {
8+
var i : Builtin.Int32
9+
}
10+
11+
sil @concrete_type_object_fail : $@convention(thin) (MyInt, MyInt) -> MyInt {
12+
bb0(%0 : $MyInt, %1 : $MyInt):
13+
%2 = builtin "generic_add"<MyInt>(%0 : $MyInt, %1 : $MyInt) : $MyInt // expected-error {{Argument of type 'MyInt' can not be passed as an argument to a Polymorphic builtin. Polymorphic builtins can only be passed arguments that are trivial builtin typed}}
14+
return %2 : $MyInt
15+
}
16+
17+
sil @concrete_type_address_fail : $@convention(thin) (@in_guaranteed MyInt, @in_guaranteed MyInt) -> @out MyInt {
18+
bb0(%0 : $*MyInt, %1 : $*MyInt, %2 : $*MyInt):
19+
%3 = builtin "generic_add"<MyInt>(%0 : $*MyInt, %1 : $*MyInt, %2 : $*MyInt) : $() // expected-error {{Argument of type 'MyInt' can not be passed as an argument to a Polymorphic builtin. Polymorphic builtins can only be passed arguments that are trivial builtin typed}}
20+
%9999 = tuple()
21+
return %9999 : $()
22+
}
23+
24+
sil @concrete_type_address_fail_mismatched_trivial_type : $@convention(thin) (@in_guaranteed Builtin.FPIEEE32, @in_guaranteed Builtin.FPIEEE32) -> @out Builtin.FPIEEE32 {
25+
bb0(%0 : $*Builtin.FPIEEE32, %1 : $*Builtin.FPIEEE32, %2 : $*Builtin.FPIEEE32):
26+
%3 = builtin "generic_add"<Builtin.FPIEEE32>(%0 : $*Builtin.FPIEEE32, %1 : $*Builtin.FPIEEE32, %2 : $*Builtin.FPIEEE32) : $() // expected-error {{Static overload 'add_FPIEEE32' does not exist for polymorphic builtin 'generic_add'. Static overload implied by passing argument of type 'Builtin.FPIEEE32'}}
27+
%9999 = tuple()
28+
return %9999 : $()
29+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-swift-frontend -parse-stdlib -emit-sil -verify %s
2+
3+
import Swift
4+
5+
struct MyInt {
6+
var i: Builtin.Int64
7+
}
8+
9+
@_transparent
10+
func _isConcrete<T>(type: T.Type) -> Bool {
11+
return Bool(_builtinBooleanLiteral: Builtin.isConcrete(type))
12+
}
13+
14+
func addVectorsNoDiagnostic(lhs: Builtin.Vec4xInt32, rhs: Builtin.Vec4xInt32) -> Builtin.Vec4xInt32 {
15+
return Builtin.generic_add(lhs, rhs)
16+
}
17+
18+
func addVectorsEmitDiagnostic(lhs: MyInt, rhs: MyInt) -> MyInt {
19+
return Builtin.generic_add(lhs, rhs) // expected-error {{Argument of type 'MyInt' can not be passed as an argument to a Polymorphic builtin. Polymorphic builtins can only be passed arguments that are trivial builtin typed}}
20+
}
21+
22+
func addVectorsGeneric<T>(lhs: T, rhs: T) -> T {
23+
return Builtin.generic_add(lhs, rhs) // expected-error {{Argument of type 'T' can not be passed as an argument to a Polymorphic builtin. Polymorphic builtins can only be passed arguments that are trivial builtin typed}}
24+
}
25+
26+
@_transparent
27+
func calleeAddVectorsGenericTransparentGuarded<T>(_ lhs: T, _ rhs: T) -> T {
28+
// This will be eliminated during constant propagation ensuring that when we
29+
// call in callerAddVectorsGenericTransparent, we do not get an error from our
30+
// underlying call.
31+
if _isConcrete(T.self) {
32+
return Builtin.generic_add(lhs, rhs)
33+
}
34+
return lhs
35+
}
36+
37+
func callerAddVectorsGenericTransparent(_ lhs: Builtin.Vec4xInt32, _ rhs: Builtin.Vec4xInt32) -> Builtin.Vec4xInt32 {
38+
// Since after transparent inlining, we have the correct type, we should get an error here.q
39+
return calleeAddVectorsGenericTransparentGuarded(lhs, rhs)
40+
}
41+
42+
@_transparent
43+
func calleeAddVectorsGenericTransparentUnguarded<T>(_ lhs: T, _ rhs: T) -> T {
44+
return Builtin.generic_add(lhs, rhs)
45+
}
46+
47+
func callerAddVectorsGenericTransparentUnguardedNoError(_ lhs: Builtin.Vec4xInt32, _ rhs: Builtin.Vec4xInt32) -> Builtin.Vec4xInt32 {
48+
return calleeAddVectorsGenericTransparentUnguarded(lhs, rhs)
49+
}
50+
51+
func callerAddVectorsGenericTransparentUnguardedError(_ lhs: MyInt, _ rhs: MyInt) -> MyInt {
52+
return calleeAddVectorsGenericTransparentUnguarded(lhs, rhs) // expected-error {{Argument of type 'MyInt' can not be passed as an argument to a Polymorphic builtin. Polymorphic builtins can only be passed arguments that are trivial builtin typed}}
53+
}

0 commit comments

Comments
 (0)