blob: 756b97eab24c096d383193f73389d45d3b8d5fcd [file] [log] [blame]
//===--- SILConstants.cpp - SIL constant representation -------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/SIL/SILConstants.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/Demangling/Demangle.h"
#include "swift/SIL/SILBuilder.h"
#include "llvm/Support/TrailingObjects.h"
using namespace swift;
namespace swift {
llvm::cl::opt<unsigned>
ConstExprLimit("constexpr-limit", llvm::cl::init(512),
llvm::cl::desc("Number of instructions interpreted in a"
" constexpr function"));
}
template <typename... T, typename... U>
static InFlightDiagnostic diagnose(ASTContext &Context, SourceLoc loc,
Diag<T...> diag, U &&... args) {
return Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
}
//===----------------------------------------------------------------------===//
// SymbolicValue implementation
//===----------------------------------------------------------------------===//
void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const {
os.indent(indent);
switch (representationKind) {
case RK_UninitMemory:
os << "uninit\n";
return;
case RK_Unknown: {
os << "unknown(" << (int)getUnknownReason() << "): ";
getUnknownNode()->dump();
return;
}
case RK_Metatype:
os << "metatype: ";
getMetatypeValue()->print(os);
os << "\n";
return;
case RK_Function: {
auto fn = getFunctionValue();
os << "fn: " << fn->getName() << ": ";
os << Demangle::demangleSymbolAsString(fn->getName());
os << "\n";
return;
}
case RK_Integer:
case RK_IntegerInline:
os << "int: " << getIntegerValue() << "\n";
return;
case RK_String:
os << "string: \"" << getStringValue() << "\"\n";
return;
case RK_Aggregate: {
ArrayRef<SymbolicValue> elements = getAggregateValue();
switch (elements.size()) {
case 0:
os << "agg: 0 elements []\n";
return;
case 1:
os << "agg: 1 elt: ";
elements[0].print(os, indent + 2);
return;
default:
os << "agg: " << elements.size() << " elements [\n";
for (auto elt : elements)
elt.print(os, indent + 2);
os.indent(indent) << "]\n";
return;
}
}
case RK_Enum: {
auto *decl = getEnumValue();
os << "enum: ";
decl->print(os);
return;
}
case RK_EnumWithPayload: {
auto *decl = getEnumValue();
os << "enum: ";
decl->print(os);
os << ", payload: ";
getEnumPayloadValue().print(os, indent);
return;
}
case RK_DirectAddress:
case RK_DerivedAddress: {
SmallVector<unsigned, 4> accessPath;
SymbolicValueMemoryObject *memObject = getAddressValue(accessPath);
os << "Address[" << memObject->getType() << "] ";
interleave(accessPath.begin(), accessPath.end(),
[&](unsigned idx) { os << idx; }, [&]() { os << ", "; });
os << "\n";
break;
}
}
}
void SymbolicValue::dump() const { print(llvm::errs()); }
/// For constant values, return the classification of this value. We have
/// multiple forms for efficiency, but provide a simpler interface to clients.
SymbolicValue::Kind SymbolicValue::getKind() const {
switch (representationKind) {
case RK_UninitMemory:
return UninitMemory;
case RK_Unknown:
return Unknown;
case RK_Metatype:
return Metatype;
case RK_Function:
return Function;
case RK_Aggregate:
return Aggregate;
case RK_Enum:
return Enum;
case RK_EnumWithPayload:
return EnumWithPayload;
case RK_Integer:
case RK_IntegerInline:
return Integer;
case RK_String:
return String;
case RK_DirectAddress:
case RK_DerivedAddress:
return Address;
}
}
/// Clone this SymbolicValue into the specified ASTContext and return the new
/// version. This only works for valid constants.
SymbolicValue
SymbolicValue::cloneInto(ASTContext &astContext) const {
auto thisRK = representationKind;
switch (thisRK) {
case RK_UninitMemory:
case RK_Unknown:
case RK_Metatype:
case RK_Function:
assert(0 && "cloning this representation kind is not supported");
case RK_Enum:
// These have trivial inline storage, just return a copy.
return *this;
case RK_IntegerInline:
case RK_Integer:
return SymbolicValue::getInteger(getIntegerValue(), astContext);
case RK_String:
return SymbolicValue::getString(getStringValue(), astContext);
case RK_Aggregate: {
auto elts = getAggregateValue();
SmallVector<SymbolicValue, 4> results;
results.reserve(elts.size());
for (auto elt : elts)
results.push_back(elt.cloneInto(astContext));
return getAggregate(results, astContext);
}
case RK_EnumWithPayload:
return getEnumWithPayload(getEnumValue(), getEnumPayloadValue(), astContext);
case RK_DirectAddress:
case RK_DerivedAddress: {
SmallVector<unsigned, 4> accessPath;
auto *memObject = getAddressValue(accessPath);
auto *newMemObject = SymbolicValueMemoryObject::create(
memObject->getType(), memObject->getValue(), astContext);
return getAddress(newMemObject, accessPath, astContext);
}
}
}
//===----------------------------------------------------------------------===//
// SymbolicValueMemoryObject implementation
//===----------------------------------------------------------------------===//
SymbolicValueMemoryObject *
SymbolicValueMemoryObject::create(Type type, SymbolicValue value,
ASTContext &astContext) {
auto *result = astContext.Allocate(sizeof(SymbolicValueMemoryObject),
alignof(SymbolicValueMemoryObject));
new (result) SymbolicValueMemoryObject(type, value);
return (SymbolicValueMemoryObject *)result;
}
//===----------------------------------------------------------------------===//
// Integers
//===----------------------------------------------------------------------===//
SymbolicValue SymbolicValue::getInteger(int64_t value, unsigned bitWidth) {
SymbolicValue result;
result.representationKind = RK_IntegerInline;
result.value.integerInline = value;
result.auxInfo.integerBitwidth = bitWidth;
return result;
}
SymbolicValue SymbolicValue::getInteger(const APInt &value,
ASTContext &astContext) {
// In the common case, we can form an inline representation.
unsigned numWords = value.getNumWords();
if (numWords == 1)
return getInteger(value.getRawData()[0], value.getBitWidth());
// Copy the integers from the APInt into the bump pointer.
auto *words = astContext.Allocate<uint64_t>(numWords).data();
std::uninitialized_copy(value.getRawData(), value.getRawData() + numWords,
words);
SymbolicValue result;
result.representationKind = RK_Integer;
result.value.integer = words;
result.auxInfo.integerBitwidth = value.getBitWidth();
return result;
}
APInt SymbolicValue::getIntegerValue() const {
assert(getKind() == Integer);
if (representationKind == RK_IntegerInline) {
auto numBits = auxInfo.integerBitwidth;
return APInt(numBits, value.integerInline);
}
assert(representationKind == RK_Integer);
auto numBits = auxInfo.integerBitwidth;
auto numWords =
(numBits + APInt::APINT_BITS_PER_WORD - 1) / APInt::APINT_BITS_PER_WORD;
return APInt(numBits, {value.integer, numWords});
}
unsigned SymbolicValue::getIntegerValueBitWidth() const {
assert(getKind() == Integer);
assert (representationKind == RK_IntegerInline ||
representationKind == RK_Integer);
return auxInfo.integerBitwidth;
}
//===----------------------------------------------------------------------===//
// Strings
//===----------------------------------------------------------------------===//
// Returns a SymbolicValue representing a UTF-8 encoded string.
SymbolicValue SymbolicValue::getString(StringRef string,
ASTContext &astContext) {
// TODO: Could have an inline representation for strings if thre was demand,
// just store a char[8] as the storage.
auto *resultPtr = astContext.Allocate<char>(string.size()).data();
std::uninitialized_copy(string.begin(), string.end(), resultPtr);
SymbolicValue result;
result.representationKind = RK_String;
result.value.string = resultPtr;
result.auxInfo.stringNumBytes = string.size();
return result;
}
// Returns the UTF-8 encoded string underlying a SymbolicValue.
StringRef SymbolicValue::getStringValue() const {
assert(getKind() == String);
assert(representationKind == RK_String);
return StringRef(value.string, auxInfo.stringNumBytes);
}
//===----------------------------------------------------------------------===//
// Aggregates
//===----------------------------------------------------------------------===//
/// This returns a constant Symbolic value with the specified elements in it.
/// This assumes that the elements lifetime has been managed for this.
SymbolicValue SymbolicValue::getAggregate(ArrayRef<SymbolicValue> elements,
ASTContext &astContext) {
// Copy the elements into the bump pointer.
auto *resultElts =
astContext.Allocate<SymbolicValue>(elements.size()).data();
std::uninitialized_copy(elements.begin(), elements.end(), resultElts);
SymbolicValue result;
result.representationKind = RK_Aggregate;
result.value.aggregate = resultElts;
result.auxInfo.aggregateNumElements = elements.size();
return result;
}
ArrayRef<SymbolicValue> SymbolicValue::getAggregateValue() const {
assert(getKind() == Aggregate);
return ArrayRef<SymbolicValue>(value.aggregate, auxInfo.aggregateNumElements);
}
//===----------------------------------------------------------------------===//
// Unknown
//===----------------------------------------------------------------------===//
namespace swift {
/// When the value is Unknown, this contains information about the unfoldable
/// part of the computation.
struct alignas(SourceLoc) UnknownSymbolicValue final
: private llvm::TrailingObjects<UnknownSymbolicValue, SourceLoc> {
friend class llvm::TrailingObjects<UnknownSymbolicValue, SourceLoc>;
/// The value that was unfoldable.
SILNode *node;
/// A more explanatory reason for the value being unknown.
UnknownReason reason;
/// The number of elements in the call stack.
unsigned callStackSize;
static UnknownSymbolicValue *create(SILNode *node, UnknownReason reason,
ArrayRef<SourceLoc> elements,
ASTContext &astContext) {
auto byteSize =
UnknownSymbolicValue::totalSizeToAlloc<SourceLoc>(elements.size());
auto *rawMem = astContext.Allocate(byteSize, alignof(UnknownSymbolicValue));
// Placement-new the value inside the memory we just allocated.
auto value = ::new (rawMem) UnknownSymbolicValue(
node, reason, static_cast<unsigned>(elements.size()));
std::uninitialized_copy(elements.begin(), elements.end(),
value->getTrailingObjects<SourceLoc>());
return value;
}
ArrayRef<SourceLoc> getCallStack() const {
return {getTrailingObjects<SourceLoc>(), callStackSize};
}
// This is used by the llvm::TrailingObjects base class.
size_t numTrailingObjects(OverloadToken<SourceLoc>) const {
return callStackSize;
}
private:
UnknownSymbolicValue() = delete;
UnknownSymbolicValue(const UnknownSymbolicValue &) = delete;
UnknownSymbolicValue(SILNode *node, UnknownReason reason,
unsigned callStackSize)
: node(node), reason(reason), callStackSize(callStackSize) {}
};
} // namespace swift
SymbolicValue SymbolicValue::getUnknown(SILNode *node, UnknownReason reason,
llvm::ArrayRef<SourceLoc> callStack,
ASTContext &astContext) {
assert(node && "node must be present");
SymbolicValue result;
result.representationKind = RK_Unknown;
result.value.unknown =
UnknownSymbolicValue::create(node, reason, callStack, astContext);
return result;
}
ArrayRef<SourceLoc> SymbolicValue::getUnknownCallStack() const {
assert(getKind() == Unknown);
return value.unknown->getCallStack();
}
SILNode *SymbolicValue::getUnknownNode() const {
assert(getKind() == Unknown);
return value.unknown->node;
}
UnknownReason SymbolicValue::getUnknownReason() const {
assert(getKind() == Unknown);
return value.unknown->reason;
}
//===----------------------------------------------------------------------===//
// Enums
//===----------------------------------------------------------------------===//
namespace swift {
/// This is the representation of a constant enum value with payload.
struct EnumWithPayloadSymbolicValue final {
/// The enum case.
EnumElementDecl *enumDecl;
SymbolicValue payload;
EnumWithPayloadSymbolicValue(EnumElementDecl *decl, SymbolicValue payload)
: enumDecl(decl), payload(payload) {}
private:
EnumWithPayloadSymbolicValue() = delete;
EnumWithPayloadSymbolicValue(const EnumWithPayloadSymbolicValue &) = delete;
};
} // end namespace swift
/// This returns a constant Symbolic value for the enum case in `decl` with a
/// payload.
SymbolicValue
SymbolicValue::getEnumWithPayload(EnumElementDecl *decl, SymbolicValue payload,
ASTContext &astContext) {
assert(decl && payload.isConstant());
auto rawMem = astContext.Allocate(sizeof(EnumWithPayloadSymbolicValue),
alignof(EnumWithPayloadSymbolicValue));
auto enumVal = ::new (rawMem) EnumWithPayloadSymbolicValue(decl, payload);
SymbolicValue result;
result.representationKind = RK_EnumWithPayload;
result.value.enumValWithPayload = enumVal;
return result;
}
EnumElementDecl *SymbolicValue::getEnumValue() const {
if (representationKind == RK_Enum)
return value.enumVal;
assert(representationKind == RK_EnumWithPayload);
return value.enumValWithPayload->enumDecl;
}
SymbolicValue SymbolicValue::getEnumPayloadValue() const {
assert(representationKind == RK_EnumWithPayload);
return value.enumValWithPayload->payload;
}
//===----------------------------------------------------------------------===//
// Addresses
//===----------------------------------------------------------------------===//
namespace swift {
/// This is the representation of a derived address. A derived address refers
/// to a memory object along with an access path that drills into it.
struct DerivedAddressValue final
: private llvm::TrailingObjects<DerivedAddressValue, unsigned> {
friend class llvm::TrailingObjects<DerivedAddressValue, unsigned>;
SymbolicValueMemoryObject *memoryObject;
/// This is the number of indices in the derived address.
const unsigned numElements;
static DerivedAddressValue *create(SymbolicValueMemoryObject *memoryObject,
ArrayRef<unsigned> elements,
ASTContext &astContext) {
auto byteSize =
DerivedAddressValue::totalSizeToAlloc<unsigned>(elements.size());
auto *rawMem = astContext.Allocate(byteSize, alignof(DerivedAddressValue));
// Placement initialize the object.
auto dav =
::new (rawMem) DerivedAddressValue(memoryObject, elements.size());
std::uninitialized_copy(elements.begin(), elements.end(),
dav->getTrailingObjects<unsigned>());
return dav;
}
/// Return the access path for this derived address, which is an array of
/// indices drilling into the memory object.
ArrayRef<unsigned> getElements() const {
return {getTrailingObjects<unsigned>(), numElements};
}
// This is used by the llvm::TrailingObjects base class.
size_t numTrailingObjects(OverloadToken<unsigned>) const {
return numElements;
}
private:
DerivedAddressValue() = delete;
DerivedAddressValue(const DerivedAddressValue &) = delete;
DerivedAddressValue(SymbolicValueMemoryObject *memoryObject,
unsigned numElements)
: memoryObject(memoryObject), numElements(numElements) {}
};
} // end namespace swift
/// Return a symbolic value that represents the address of a memory object
/// indexed by a path.
SymbolicValue SymbolicValue::getAddress(SymbolicValueMemoryObject *memoryObject,
ArrayRef<unsigned> indices,
ASTContext &astContext) {
if (indices.empty())
return getAddress(memoryObject);
auto dav = DerivedAddressValue::create(memoryObject, indices, astContext);
SymbolicValue result;
result.representationKind = RK_DerivedAddress;
result.value.derivedAddress = dav;
return result;
}
/// Return the memory object of this reference along with any access path
/// indices involved.
SymbolicValueMemoryObject *
SymbolicValue::getAddressValue(SmallVectorImpl<unsigned> &accessPath) const {
assert(getKind() == Address);
accessPath.clear();
if (representationKind == RK_DirectAddress)
return value.directAddress;
assert(representationKind == RK_DerivedAddress);
auto *dav = value.derivedAddress;
// The first entry is the object ID, the rest are indices in the accessPath.
accessPath.assign(dav->getElements().begin(), dav->getElements().end());
return dav->memoryObject;
}
/// Return just the memory object for an address value.
SymbolicValueMemoryObject *SymbolicValue::getAddressValueMemoryObject() const {
if (representationKind == RK_DirectAddress)
return value.directAddress;
assert(representationKind == RK_DerivedAddress);
return value.derivedAddress->memoryObject;
}
//===----------------------------------------------------------------------===//
// Higher level code
//===----------------------------------------------------------------------===//
/// The SIL location for operations we process are usually deep in the bowels
/// of inlined code from opaque libraries, which are all implementation details
/// to the user. As such, walk the inlining location of the specified node to
/// return the first location *outside* opaque libraries.
static SILDebugLocation skipInternalLocations(SILDebugLocation loc) {
auto ds = loc.getScope();
if (!ds || loc.getLocation().getSourceLoc().isValid())
return loc;
// Zip through inlined call site information that came from the
// implementation guts of the library. We want to report the message inside
// the user's code, not in the guts we inlined through.
for (; auto ics = ds->InlinedCallSite; ds = ics) {
// If we found a valid inlined-into location, then we are good.
if (ds->Loc.getSourceLoc().isValid())
return SILDebugLocation(ds->Loc, ds);
if (SILFunction *F = ds->getInlinedFunction()) {
if (F->getLocation().getSourceLoc().isValid())
break;
}
}
if (ds->Loc.getSourceLoc().isValid())
return SILDebugLocation(ds->Loc, ds);
return loc;
}
/// Dig through single element aggregates, return the ultimate thing inside of
/// it. This is useful when dealing with integers and floats, because they
/// are often wrapped in single-element struct wrappers.
SymbolicValue SymbolicValue::lookThroughSingleElementAggregates() const {
auto result = *this;
while (1) {
if (result.getKind() != Aggregate)
return result;
auto elts = result.getAggregateValue();
if (elts.size() != 1)
return result;
result = elts[0];
}
}
/// Emits an explanatory note if there is useful information to note or if there
/// is an interesting SourceLoc to point at.
/// Returns true if a diagnostic was emitted.
static bool emitNoteDiagnostic(SILInstruction *badInst, UnknownReason reason,
SILLocation fallbackLoc) {
auto loc = skipInternalLocations(badInst->getDebugLocation()).getLocation();
if (loc.isNull()) {
// If we have important clarifying information, make sure to emit it.
if (reason == UnknownReason::Default || fallbackLoc.isNull())
return false;
loc = fallbackLoc;
}
auto &ctx = badInst->getModule().getASTContext();
auto sourceLoc = loc.getSourceLoc();
switch (reason) {
case UnknownReason::Default:
diagnose(ctx, sourceLoc, diag::constexpr_unknown_reason_default)
.highlight(loc.getSourceRange());
break;
case UnknownReason::TooManyInstructions:
// TODO: Should pop up a level of the stack trace.
diagnose(ctx, sourceLoc, diag::constexpr_too_many_instructions,
ConstExprLimit)
.highlight(loc.getSourceRange());
break;
case UnknownReason::Loop:
diagnose(ctx, sourceLoc, diag::constexpr_loop)
.highlight(loc.getSourceRange());
break;
case UnknownReason::Overflow:
diagnose(ctx, sourceLoc, diag::constexpr_overflow)
.highlight(loc.getSourceRange());
break;
case UnknownReason::Trap:
diagnose(ctx, sourceLoc, diag::constexpr_trap)
.highlight(loc.getSourceRange());
break;
}
return true;
}
/// Given that this is an 'Unknown' value, emit diagnostic notes providing
/// context about what the problem is.
void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) {
auto badInst = dyn_cast<SILInstruction>(getUnknownNode());
if (!badInst)
return;
bool emittedFirstNote = emitNoteDiagnostic(badInst, getUnknownReason(),
fallbackLoc);
auto sourceLoc = fallbackLoc.getSourceLoc();
auto &module = badInst->getModule();
if (sourceLoc.isInvalid()) {
diagnose(module.getASTContext(), sourceLoc, diag::constexpr_not_evaluable);
return;
}
for (auto &sourceLoc : llvm::reverse(getUnknownCallStack())) {
// Skip unknown sources.
if (!sourceLoc.isValid())
continue;
auto diag = emittedFirstNote ? diag::constexpr_called_from
: diag::constexpr_not_evaluable;
diagnose(module.getASTContext(), sourceLoc, diag);
emittedFirstNote = true;
}
}
/// Returns the element of `aggregate` specified by the access path.
///
/// This is a helper for `SymbolicValueMemoryObject::getIndexedElement`. See
/// there for more detailed documentation.
static SymbolicValue getIndexedElement(SymbolicValue aggregate,
ArrayRef<unsigned> accessPath,
Type type) {
// We're done if we've run out of access path.
if (accessPath.empty())
return aggregate;
// Everything inside uninit memory is uninit memory.
if (aggregate.getKind() == SymbolicValue::UninitMemory)
return SymbolicValue::getUninitMemory();
assert(aggregate.getKind() == SymbolicValue::Aggregate &&
"the accessPath is invalid for this type");
unsigned elementNo = accessPath.front();
SymbolicValue elt = aggregate.getAggregateValue()[elementNo];
Type eltType;
if (auto *decl = type->getStructOrBoundGenericStruct()) {
auto it = decl->getStoredProperties().begin();
std::advance(it, elementNo);
eltType = (*it)->getType();
} else if (auto tuple = type->getAs<TupleType>()) {
assert(elementNo < tuple->getNumElements() && "invalid index");
eltType = tuple->getElement(elementNo).getType();
} else {
llvm_unreachable("the accessPath is invalid for this type");
}
return getIndexedElement(elt, accessPath.drop_front(), eltType);
}
/// Given that this memory object contains an aggregate value like
/// {{1, 2}, 3}, and given an access path like [0,1], return the indexed
/// element, e.g. "2" in this case.
///
/// Returns uninit memory if the access path points at or into uninit memory.
///
/// Precondition: The access path must be valid for this memory object's type.
SymbolicValue
SymbolicValueMemoryObject::getIndexedElement(ArrayRef<unsigned> accessPath) {
return ::getIndexedElement(value, accessPath, type);
}
/// Returns `aggregate` with the element specified by the access path set to
/// `newElement`.
///
/// This is a helper for `SymbolicValueMemoryObject::setIndexedElement`. See
/// there for more detailed documentation.
static SymbolicValue setIndexedElement(SymbolicValue aggregate,
ArrayRef<unsigned> accessPath,
SymbolicValue newElement, Type type,
ASTContext &astCtx) {
// We're done if we've run out of access path.
if (accessPath.empty())
return newElement;
// If we have an uninit memory, then scalarize it into an aggregate to
// continue. This happens when memory objects are initialized piecewise.
if (aggregate.getKind() == SymbolicValue::UninitMemory) {
unsigned numMembers;
// We need to have either a struct or a tuple type.
if (auto *decl = type->getStructOrBoundGenericStruct()) {
numMembers = std::distance(decl->getStoredProperties().begin(),
decl->getStoredProperties().end());
} else if (auto tuple = type->getAs<TupleType>()) {
numMembers = tuple->getNumElements();
} else {
llvm_unreachable("the accessPath is invalid for this type");
}
SmallVector<SymbolicValue, 4> newElts(numMembers,
SymbolicValue::getUninitMemory());
aggregate = SymbolicValue::getAggregate(newElts, astCtx);
}
assert(aggregate.getKind() == SymbolicValue::Aggregate &&
"the accessPath is invalid for this type");
unsigned elementNo = accessPath.front();
ArrayRef<SymbolicValue> oldElts = aggregate.getAggregateValue();
Type eltType;
if (auto *decl = type->getStructOrBoundGenericStruct()) {
auto it = decl->getStoredProperties().begin();
std::advance(it, elementNo);
eltType = (*it)->getType();
} else if (auto tuple = type->getAs<TupleType>()) {
assert(elementNo < tuple->getNumElements() && "invalid index");
eltType = tuple->getElement(elementNo).getType();
} else {
llvm_unreachable("the accessPath is invalid for this type");
}
// Update the indexed element of the aggregate.
SmallVector<SymbolicValue, 4> newElts(oldElts.begin(), oldElts.end());
newElts[elementNo] = setIndexedElement(newElts[elementNo],
accessPath.drop_front(), newElement,
eltType, astCtx);
aggregate = SymbolicValue::getAggregate(newElts, astCtx);
return aggregate;
}
/// Given that this memory object contains an aggregate value like
/// {{1, 2}, 3}, given an access path like [0,1], and given a new element like
/// "4", set the indexed element to the specified scalar, producing {{1, 4},
/// 3} in this case.
///
/// Precondition: The access path must be valid for this memory object's type.
void SymbolicValueMemoryObject::setIndexedElement(
ArrayRef<unsigned> accessPath, SymbolicValue newElement,
ASTContext &astCtx) {
value = ::setIndexedElement(value, accessPath, newElement, type, astCtx);
}