Skip to content

Convert ArrayExpr to not use callWitness() or generate a SemanticExpr. #23618

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
merged 5 commits into from
Mar 28, 2019
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
11 changes: 11 additions & 0 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2207,6 +2207,7 @@ class TupleExpr final : public Expr,
class CollectionExpr : public Expr {
SourceLoc LBracketLoc;
SourceLoc RBracketLoc;
ConcreteDeclRef Initializer;

Expr *SemanticExpr = nullptr;

Expand Down Expand Up @@ -2283,6 +2284,14 @@ class CollectionExpr : public Expr {
e->getKind() <= ExprKind::Last_CollectionExpr;
}

/// Retrieve the initializer that will be used to construct the 'array'
/// literal from the result of the initializer.
ConcreteDeclRef getInitializer() const { return Initializer; }

/// Set the initializer that will be used to construct the 'array' literal.
void setInitializer(ConcreteDeclRef initializer) {
Initializer = initializer;
}
};

/// An array literal expression [a, b, c].
Expand Down Expand Up @@ -2313,6 +2322,8 @@ class ArrayExpr final : public CollectionExpr,
static bool classof(const Expr *e) {
return e->getKind() == ExprKind::Array;
}

Type getElementType();
};

/// A dictionary literal expression [a : x, b : y, c : z].
Expand Down
2 changes: 2 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2068,6 +2068,8 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
}
void visitArrayExpr(ArrayExpr *E) {
printCommon(E, "array_expr");
PrintWithColorRAII(OS, LiteralValueColor) << " initializer=";
E->getInitializer().dump(PrintWithColorRAII(OS, LiteralValueColor).getOS());
for (auto elt : E->getElements()) {
OS << '\n';
printRec(elt);
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,19 @@ ArrayExpr *ArrayExpr::create(ASTContext &C, SourceLoc LBracketLoc,
return new (Mem) ArrayExpr(LBracketLoc, Elements, CommaLocs, RBracketLoc, Ty);
}

Type ArrayExpr::getElementType() {
auto init = getInitializer();
if (!init)
return Type();

auto *decl = cast<ConstructorDecl>(init.getDecl());
return decl->getMethodInterfaceType()
->getAs<AnyFunctionType>()
->getParams()[0]
.getPlainType()
.subst(init.getSubstitutions());
}

DictionaryExpr *DictionaryExpr::create(ASTContext &C, SourceLoc LBracketLoc,
ArrayRef<Expr*> Elements,
ArrayRef<SourceLoc> CommaLocs,
Expand Down
7 changes: 7 additions & 0 deletions lib/IDE/ExprContextAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,12 @@ class ExprContextAnalyzer {
analyzeApplyExpr(Parent);
break;
}
case ExprKind::Array: {
if (auto type = ParsedExpr->getType()) {
recordPossibleType(type);
}
break;
}
case ExprKind::Assign: {
auto *AE = cast<AssignExpr>(Parent);

Expand Down Expand Up @@ -744,6 +750,7 @@ class ExprContextAnalyzer {
case ExprKind::PrefixUnary:
case ExprKind::Assign:
case ExprKind::Subscript:
case ExprKind::Array:
return true;
case ExprKind::Tuple: {
auto ParentE = Parent.getAsExpr();
Expand Down
36 changes: 0 additions & 36 deletions lib/SILGen/RValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,42 +308,6 @@ class RValue {
/// be returned. Otherwise, an object will be returned. So this is a
/// convenient way to determine if an RValue needs an address.
SILType getLoweredImplodedTupleType(SILGenFunction &SGF) const &;

/// Rewrite the type of this r-value.
void rewriteType(CanType newType) & {
#ifndef NDEBUG
static const auto areSimilarTypes = [](CanType l, CanType r) {
if (l == r) return true;

// Allow function types to disagree about 'noescape'.
if (auto lf = dyn_cast<FunctionType>(l)) {
if (auto rf = dyn_cast<FunctionType>(r)) {
auto lParams = lf.getParams();
auto rParams = rf.getParams();
return AnyFunctionType::equalParams(lParams, rParams) &&
lf.getResult() == rf.getResult() &&
lf->getExtInfo().withNoEscape(false) ==
rf->getExtInfo().withNoEscape(false);
}
}
return false;
};

static const auto isSingleElementTuple = [](CanType type, CanType eltType) {
if (auto tupleType = dyn_cast<TupleType>(type)) {
return tupleType->getNumElements() == 1 &&
areSimilarTypes(tupleType.getElementType(0), eltType);
}
return false;
};

// We only allow a very modest set of changes to a type.
assert(areSimilarTypes(newType, type) ||
isSingleElementTuple(newType, type) ||
isSingleElementTuple(type, newType));
#endif
type = newType;
}

/// Emit an equivalent value with independent ownership.
RValue copy(SILGenFunction &SGF, SILLocation loc) const &;
Expand Down
19 changes: 0 additions & 19 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5635,20 +5635,7 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) {
}
}

// Helper routine to add an argument label if we need one.
auto relabelArgument = [&](ConcreteDeclRef callee, RValue &arg) {
auto name = callee.getDecl()->getFullName();
auto argLabels = name.getArgumentNames();
if (argLabels.size() == 1 && !argLabels[0].empty() &&
!isa<TupleType>(arg.getType())) {
Type newType = TupleType::get({TupleTypeElt(arg.getType(), argLabels[0])},
getASTContext());
arg.rewriteType(newType->getCanonicalType());
}
};

// Call the builtin initializer.
relabelArgument(builtinInit, builtinLiteralArgs);
RValue builtinLiteral =
emitApplyAllocatingInitializer(literal, builtinInit,
std::move(builtinLiteralArgs),
Expand All @@ -5659,7 +5646,6 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) {
if (!init) return builtinLiteral;

// Otherwise, perform the second initialization step.
relabelArgument(init, builtinLiteral);
RValue result = emitApplyAllocatingInitializer(literal, init,
std::move(builtinLiteral),
literal->getType(), C);
Expand Down Expand Up @@ -6189,14 +6175,9 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set,
values.addArbitrary(std::move(setValue));

if (!subscriptIndices.isNull()) {
unsigned paramIndex = 1;
for (auto &component : std::move(subscriptIndices).getSources()) {
auto param = accessType.getParams()[paramIndex++];
auto paramType = param.getParameterType();

auto argLoc = component.getKnownRValueLocation();
RValue &&arg = std::move(component).asKnownRValue(*this);
arg.rewriteType(paramType);
values.add(argLoc, std::move(arg));
}
}
Expand Down
62 changes: 60 additions & 2 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,8 @@ namespace {
RValue visitKeyPathExpr(KeyPathExpr *E, SGFContext C);
RValue visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E,
SGFContext C);
RValue visitCollectionExpr(CollectionExpr *E, SGFContext C);
RValue visitArrayExpr(ArrayExpr *E, SGFContext C);
RValue visitDictionaryExpr(DictionaryExpr *E, SGFContext C);
RValue visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E,
SGFContext C);
RValue visitInjectIntoOptionalExpr(InjectIntoOptionalExpr *E, SGFContext C);
Expand Down Expand Up @@ -3652,7 +3653,64 @@ visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, SGFContext C) {
llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch.");
}

RValue RValueEmitter::visitCollectionExpr(CollectionExpr *E, SGFContext C) {
RValue RValueEmitter::visitArrayExpr(ArrayExpr *E, SGFContext C) {
auto loc = SILLocation(E);
ArgumentScope scope(SGF, loc);

CanType elementType = E->getElementType()->getCanonicalType();
CanType arrayTy = ArraySliceType::get(elementType)->getCanonicalType();
VarargsInfo varargsInfo =
emitBeginVarargs(SGF, loc, elementType, arrayTy,
E->getNumElements(), {});

// Cleanups for any elements that have been initialized so far.
SmallVector<CleanupHandle, 8> cleanups;

for (unsigned index : range(E->getNumElements())) {
auto destAddr = varargsInfo.getBaseAddress();
if (index != 0) {
SILValue indexValue = SGF.B.createIntegerLiteral(
loc, SILType::getBuiltinWordType(SGF.getASTContext()), index);
destAddr = SGF.B.createIndexAddr(loc, destAddr, indexValue);
}
auto &destTL = varargsInfo.getBaseTypeLowering();
// Create a dormant cleanup for the value in case we exit before the
// full array has been constructed.

CleanupHandle destCleanup = CleanupHandle::invalid();
if (!destTL.isTrivial()) {
destCleanup = SGF.enterDestroyCleanup(destAddr);
SGF.Cleanups.setCleanupState(destCleanup, CleanupState::Dormant);
cleanups.push_back(destCleanup);
}

TemporaryInitialization init(destAddr, destCleanup);

ArgumentSource(E->getElements()[index])
.forwardInto(SGF, varargsInfo.getBaseAbstractionPattern(), &init,
destTL);
}

// Kill the per-element cleanups. The array will take ownership of them.
for (auto destCleanup : cleanups)
SGF.Cleanups.setCleanupState(destCleanup, CleanupState::Dead);

RValue arg(SGF, loc, arrayTy,
emitEndVarargs(SGF, loc, std::move(varargsInfo)));

arg = scope.popPreservingValue(std::move(arg));

// If we're building an array, we don't have to call the initializer;
// we've already built one.
if (arrayTy->isEqual(E->getType()))
return arg;

// Call the builtin initializer.
return SGF.emitApplyAllocatingInitializer(
loc, E->getInitializer(), std::move(arg), E->getType(), C);
}

RValue RValueEmitter::visitDictionaryExpr(DictionaryExpr *E, SGFContext C) {
return visit(E->getSemanticExpr(), C);
}

Expand Down
68 changes: 10 additions & 58 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2786,71 +2786,23 @@ namespace {
ConformanceCheckFlags::InExpression);
assert(conformance && "Type does not conform to protocol?");

// Call the witness that builds the array literal.
// FIXME: callWitness() may end up re-doing some work we already did
// to convert the array literal elements to the element type. It would
// be nicer to re-use them.

// FIXME: This location info is bogus.
Expr *typeRef = TypeExpr::createImplicitHack(expr->getLoc(), arrayTy,
tc.Context);
cs.cacheExprTypes(typeRef);

DeclName name(tc.Context, DeclBaseName::createConstructor(),
{ tc.Context.Id_arrayLiteral });

// Coerce the array elements to be rvalues, so that other type-checker
// code that attempts to peephole the AST doesn't have to re-load the
// elements (and break the invariant that lvalue nodes only get their
// access kind set once).
for (auto &element : expr->getElements()) {
element = cs.coerceToRValue(element);
}

// Restructure the argument to provide the appropriate labels in the
// tuple.
SmallVector<TupleTypeElt, 4> typeElements;
SmallVector<Identifier, 4> names;
bool first = true;
for (auto elt : expr->getElements()) {
if (first) {
typeElements.push_back(TupleTypeElt(cs.getType(elt),
tc.Context.Id_arrayLiteral));
names.push_back(tc.Context.Id_arrayLiteral);
ConcreteDeclRef witness =
findNamedWitnessImpl(tc, dc, arrayTy->getRValueType(), arrayProto,
name, diag::array_protocol_broken, conformance);
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
return nullptr;
expr->setInitializer(witness);

first = false;
continue;
}
auto elementType = expr->getElementType();

typeElements.push_back(cs.getType(elt));
names.push_back(Identifier());
for (auto &element : expr->getElements()) {
element = coerceToType(element, elementType,
cs.getConstraintLocator(element));
}

Type argType = TupleType::get(typeElements, tc.Context);
assert(isa<TupleType>(argType.getPointer()));

Expr *arg =
TupleExpr::create(tc.Context, SourceLoc(),
expr->getElements(),
names,
{ },
SourceLoc(), /*HasTrailingClosure=*/false,
/*Implicit=*/true,
argType);

cs.cacheExprTypes(arg);

cs.setExprTypes(typeRef);
cs.setExprTypes(arg);

Expr *result = tc.callWitness(typeRef, dc, arrayProto, *conformance,
name, arg, diag::array_protocol_broken);
if (!result)
return nullptr;

cs.cacheExprTypes(result);

expr->setSemanticExpr(result);
return expr;
}

Expand Down
6 changes: 3 additions & 3 deletions test/IDE/complete_unresolved_members.swift
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,12 @@ func f() -> SomeEnum1 {
return .#^UNRESOLVED_27^#
}

let TopLevelVar1 = OptionSetTaker7([.#^UNRESOLVED_28^#], Op2: [.Option4])
let TopLevelVar1 = OptionSetTaker7([.#^UNRESOLVED_28^#], [.Option4])

let TopLevelVar2 = OptionSetTaker1([.#^UNRESOLVED_29^#])

let TopLevelVar3 = OptionSetTaker7([.Option1], Op2: [.#^UNRESOLVED_30^#])
let TopLevelVar4 = OptionSetTaker7([.Option1], Op2: [.Option4, .#^UNRESOLVED_31^#])
let TopLevelVar3 = OptionSetTaker7([.Option1], [.#^UNRESOLVED_30^#])
let TopLevelVar4 = OptionSetTaker7([.Option1], [.Option4, .#^UNRESOLVED_31^#])

let _: [SomeEnum1] = [.#^UNRESOLVED_32^#]
let _: [SomeEnum1] = [.South, .#^UNRESOLVED_33^#]
Expand Down
22 changes: 22 additions & 0 deletions test/Interpreter/arrays.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,25 @@ test()
let mdaPerf = [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]]
print(mdaPerf)
// CHECK: {{\[}}[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]]

class Deinitable {
deinit {
print("deinit called")
}
}

enum E : Error {
case error
}

func throwingFunc() throws -> Deinitable {
throw E.error
}

do {
let array = try [Deinitable(), throwingFunc()]
} catch {
// CHECK: deinit called
// CHECK: error thrown
print("error thrown")
}
Loading