Skip to content

[polymorphic-builtins] Teach dataflow diagnostics how to emit an erro… #27284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion include/swift/AST/Builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ class IntrinsicInfo {

/// Turn a string like "release" into the LLVM enum.
llvm::AtomicOrdering decodeLLVMAtomicOrdering(StringRef O);


/// Returns true if the builtin with ID \p ID has a defined static overload for
/// the type \p Ty.
bool canBuiltinBeOverloadedForType(BuiltinValueKind ID, Type Ty);
}

#endif
10 changes: 10 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,16 @@ ERROR(oslog_property_not_constant, none, "'OSLogInterpolation.%0' is not a "
ERROR(global_string_pointer_on_non_constant, none, "globalStringTablePointer "
"builtin must used only on string literals", ())

ERROR(polymorphic_builtin_passed_non_trivial_non_builtin_type, none, "Argument "
"of type %0 can not be passed as an argument to a Polymorphic "
"builtin. Polymorphic builtins can only be passed arguments that are "
"trivial builtin typed", (Type))

ERROR(polymorphic_builtin_passed_type_without_static_overload, none, "Static"
" overload %0 does not exist for polymorphic builtin '%1'. Static "
"overload implied by passing argument of type %2",
(Identifier, StringRef, Type))

#ifndef DIAG_NO_UNDEF
# if defined(DIAG)
# undef DIAG
Expand Down
26 changes: 20 additions & 6 deletions include/swift/SIL/InstructionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,23 +149,37 @@ void findClosuresForFunctionValue(SILValue V,
/// NOTE: If we perform this transformation, our builtin will no longer have any
/// substitutions since we only substitute to concrete static overloads.
struct PolymorphicBuiltinSpecializedOverloadInfo {
const BuiltinInfo *builtinInfo;
Identifier staticOverloadIdentifier;
SmallVector<SILType, 8> argTypes;
SILType resultType;
bool hasOutParam = false;
bool hasOutParam;

#ifndef NDEBUG
private:
bool isInitialized = false;
#endif
bool isInitialized;

public:
PolymorphicBuiltinSpecializedOverloadInfo() = default;
#endif

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

/// Returns true if we were able to map the polymorphic builtin to a static
/// overload. False otherwise.
///
/// NOTE: This does not mean that the static overload actually exists.
bool init(BuiltinInst *bi);

bool doesOverloadExist() const {
CanBuiltinType builtinType = argTypes.front().getAs<BuiltinType>();
return canBuiltinBeOverloadedForType(builtinInfo->ID, builtinType);
}

private:
bool init(SILFunction *fn, BuiltinValueKind builtinKind,
ArrayRef<SILType> oldOperandTypes, SILType oldResultType);
};

/// Given a polymorphic builtin \p bi, analyze its types and create a builtin
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,13 @@ inline bool isBuiltinTypeOverloaded(Type T, OverloadedBuiltinKind OK) {
llvm_unreachable("bad overloaded builtin kind");
}

bool swift::canBuiltinBeOverloadedForType(BuiltinValueKind ID, Type Ty) {
if (ID == BuiltinValueKind::None)
return false;

return isBuiltinTypeOverloaded(Ty, OverloadedBuiltinKinds[unsigned(ID)]);
}

/// Table of string intrinsic names indexed by enum value.
static const char *const IntrinsicNameTable[] = {
"not_intrinsic",
Expand Down
6 changes: 6 additions & 0 deletions lib/SIL/InstructionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,12 @@ bool PolymorphicBuiltinSpecializedOverloadInfo::init(

auto &ctx = fn->getASTContext();
staticOverloadIdentifier = ctx.getIdentifier(staticOverloadName);

// Ok, we have our overload identifier. Grab the builtin info from the
// cache. If we did not actually found a valid builtin value kind for our
// overload, then we do not have a static overload for the passed in types, so
// return false.
builtinInfo = &fn->getModule().getBuiltinInfo(staticOverloadIdentifier);
return true;
}

Expand Down
77 changes: 71 additions & 6 deletions lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,22 @@
//
//===----------------------------------------------------------------------===//

#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/ConstExpr.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Stmt.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/SILConstants.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILLocation.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SIL/SILConstants.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/ConstExpr.h"

using namespace swift;

Expand Down Expand Up @@ -175,6 +176,67 @@ static void diagnosePoundAssert(const SILInstruction *I,
}
}

static void diagnoseUnspecializedPolymorphicBuiltins(SILInstruction *inst) {
// We only validate if we are in a non-transparent function.
if (inst->getFunction()->isTransparent())
return;

auto *bi = dyn_cast<BuiltinInst>(inst);
if (!bi)
return;

auto kind = bi->getBuiltinKind();
if (!kind)
return;

if (!isPolymorphicBuiltin(*kind))
return;

const auto &builtinInfo = bi->getBuiltinInfo();

// First that the parameters were acceptable so we can emit a nice error to
// guide the user.
for (SILValue value : bi->getOperandValues()) {
SILType type = value->getType();
SourceLoc loc;
if (auto *inst = value->getDefiningInstruction()) {
loc = inst->getLoc().getSourceLoc();
} else {
loc = bi->getLoc().getSourceLoc();
}

if (!type.is<BuiltinType>() || !type.isTrivial(*bi->getFunction())) {
diagnose(bi->getModule().getASTContext(), loc,
diag::polymorphic_builtin_passed_non_trivial_non_builtin_type,
type.getASTType());
return;
}
}

// Ok, we have a valid type for a polymorphic builtin. Make sure we actually
// have a static overload for this type.
PolymorphicBuiltinSpecializedOverloadInfo overloadInfo;
bool ableToMapToStaticOverload = overloadInfo.init(bi);
(void)ableToMapToStaticOverload;
assert(ableToMapToStaticOverload);
if (!overloadInfo.doesOverloadExist()) {
diagnose(bi->getModule().getASTContext(), bi->getLoc().getSourceLoc(),
diag::polymorphic_builtin_passed_type_without_static_overload,
overloadInfo.staticOverloadIdentifier,
getBuiltinName(builtinInfo.ID),
overloadInfo.argTypes.front().getASTType());
return;
}

// Otherwise, something happen that we did not understand. This can only
// happen if we specialize the generic type in the builtin /after/ constant
// propagation runs at -Onone but before dataflow diagnostics. This is an
// error in implementation, so we assert.
llvm_unreachable("Found generic builtin with known static overload that it "
"could be transformed to. Did this builtin get its generic "
"type specialized /after/ constant propagation?");
}

namespace {
class EmitDFDiagnostics : public SILFunctionTransform {
~EmitDFDiagnostics() override {}
Expand All @@ -186,11 +248,13 @@ class EmitDFDiagnostics : public SILFunctionTransform {
return;

SILModule &M = getFunction()->getModule();
for (auto &BB : *getFunction())
for (auto &BB : *getFunction()) {
for (auto &I : BB) {
diagnoseUnreachable(&I, M.getASTContext());
diagnoseStaticReports(&I, M);
diagnoseUnspecializedPolymorphicBuiltins(&I);
}
}

if (M.getASTContext().LangOpts.EnableExperimentalStaticAssert) {
SymbolicValueBumpAllocator allocator;
Expand All @@ -202,6 +266,7 @@ class EmitDFDiagnostics : public SILFunctionTransform {
}
}
};

} // end anonymous namespace


Expand Down
29 changes: 29 additions & 0 deletions test/SILOptimizer/polymorphic_builtins_diagnostics.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %target-sil-opt -module-name Swift -dataflow-diagnostics -verify %s

sil_stage raw

import Builtin

struct MyInt {
var i : Builtin.Int32
}

sil @concrete_type_object_fail : $@convention(thin) (MyInt, MyInt) -> MyInt {
bb0(%0 : $MyInt, %1 : $MyInt):
%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}}
return %2 : $MyInt
}

sil @concrete_type_address_fail : $@convention(thin) (@in_guaranteed MyInt, @in_guaranteed MyInt) -> @out MyInt {
bb0(%0 : $*MyInt, %1 : $*MyInt, %2 : $*MyInt):
%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}}
%9999 = tuple()
return %9999 : $()
}

sil @concrete_type_address_fail_mismatched_trivial_type : $@convention(thin) (@in_guaranteed Builtin.FPIEEE32, @in_guaranteed Builtin.FPIEEE32) -> @out Builtin.FPIEEE32 {
bb0(%0 : $*Builtin.FPIEEE32, %1 : $*Builtin.FPIEEE32, %2 : $*Builtin.FPIEEE32):
%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'}}
%9999 = tuple()
return %9999 : $()
}
53 changes: 53 additions & 0 deletions test/SILOptimizer/polymorphic_builtins_diagnostics.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// RUN: %target-swift-frontend -parse-stdlib -emit-sil -verify %s

import Swift

struct MyInt {
var i: Builtin.Int64
}

@_transparent
func _isConcrete<T>(type: T.Type) -> Bool {
return Bool(_builtinBooleanLiteral: Builtin.isConcrete(type))
}

func addVectorsNoDiagnostic(lhs: Builtin.Vec4xInt32, rhs: Builtin.Vec4xInt32) -> Builtin.Vec4xInt32 {
return Builtin.generic_add(lhs, rhs)
}

func addVectorsEmitDiagnostic(lhs: MyInt, rhs: MyInt) -> MyInt {
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}}
}

func addVectorsGeneric<T>(lhs: T, rhs: T) -> T {
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}}
}

@_transparent
func calleeAddVectorsGenericTransparentGuarded<T>(_ lhs: T, _ rhs: T) -> T {
// This will be eliminated during constant propagation ensuring that when we
// call in callerAddVectorsGenericTransparent, we do not get an error from our
// underlying call.
if _isConcrete(T.self) {
return Builtin.generic_add(lhs, rhs)
}
return lhs
}

func callerAddVectorsGenericTransparent(_ lhs: Builtin.Vec4xInt32, _ rhs: Builtin.Vec4xInt32) -> Builtin.Vec4xInt32 {
// Since after transparent inlining, we have the correct type, we should get an error here.q
return calleeAddVectorsGenericTransparentGuarded(lhs, rhs)
}

@_transparent
func calleeAddVectorsGenericTransparentUnguarded<T>(_ lhs: T, _ rhs: T) -> T {
return Builtin.generic_add(lhs, rhs)
}

func callerAddVectorsGenericTransparentUnguardedNoError(_ lhs: Builtin.Vec4xInt32, _ rhs: Builtin.Vec4xInt32) -> Builtin.Vec4xInt32 {
return calleeAddVectorsGenericTransparentUnguarded(lhs, rhs)
}

func callerAddVectorsGenericTransparentUnguardedError(_ lhs: MyInt, _ rhs: MyInt) -> MyInt {
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}}
}