Repository: apple/swift
File: lib/ClangImporter/ClangImporter.cpp (C++)

Code before:
  return result;
}

static std::set<StringRef>
getConditionalEscapableAttrParams(const clang::RecordDecl *decl) {
  return getConditionalAttrParams(decl, "escapable_if:");
}

static std::set<StringRef>
getConditionalCopyableAttrParams(const clang::RecordDecl *decl) {
  return getConditionalAttrParams(decl, "copyable_if:");
}

CxxEscapability
ClangTypeEscapability::evaluate(Evaluator &evaluator,
                                EscapabilityLookupDescriptor desc) const {

  // Escapability inference rules:
  // - array and vector types have the same escapability as their element type
  // - pointer and reference types are currently imported as unknown
  // (importing them as non-escapable broke backward compatibility)
  // - a record type is escapable or non-escapable if it is explicitly annotated
  // as such
  // - a record type is escapable if it is annotated with SWIFT_ESCAPABLE_IF()
  // and none of the annotation arguments are non-escapable
  // - an aggregate or a record that is not a cxxrecord are escapable if none of
  // their fields or bases are non-escapable (as long as they have a definition)
  // - in all other cases, the record has unknown escapability (e.g. no
  // escapability annotations, malformed escapability annotations)

  bool hasUnknown = false;
  auto desugared = desc.type->getUnqualifiedDesugaredType();
  if (const auto *recordType = desugared->getAs<clang::RecordType>()) {
    auto recordDecl = recordType->getDecl();
    // If the root type has a SWIFT_ESCAPABLE annotation, we import the type as
    // Escapable without entering the loop
    if (hasEscapableAttr(recordDecl))
      return CxxEscapability::Escapable;
  }

  llvm::SmallVector<const clang::Type *, 4> stack;
  // Keep track of Types we've seen to avoid cycles
  llvm::SmallDenseSet<const clang::Type *, 4> seen;

  auto maybePushToStack = [&](const clang::Type *type) {
    auto desugared = type->getUnqualifiedDesugaredType();
    if (seen.insert(desugared).second)
      stack.push_back(desugared);
  };

  maybePushToStack(desc.type);

Review comment (refactor):
nit: simplify wording

```suggestion
  // - an aggregate or non-cxx record is escapable if none of
```

I think it might also be helpful to spell out what it means to be an "aggregate" record. In my mind I read this as basically being "a C-like record", i.e., not having user-defined behavior for copy construction, but maybe that's inaccurate/incomplete.

Also, can you look up/document the reason why we are distinguish aggregate records from non-aggregate/"complex" records?

Code after:
    return result;
  for (auto attr : decl->getAttrs()) {
    if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
      StringRef params = swiftAttr->getAttribute();
      if (params.consume_front(attrName)) {
        auto commaPos = params.find(',');
        StringRef nextParam = params.take_front(commaPos);
        while (!nextParam.empty() && commaPos != StringRef::npos) {
          result.insert(nextParam.trim());
          params = params.drop_front(nextParam.size() + 1);
          commaPos = params.find(',');
          nextParam = params.take_front(commaPos);
        }
      }
    }
  }
  return result;
}

static std::set<StringRef>
getConditionalEscapableAttrParams(const clang::RecordDecl *decl) {
  return getConditionalAttrParams(decl, "escapable_if:");
}

static std::set<StringRef>
getConditionalCopyableAttrParams(const clang::RecordDecl *decl) {
  return getConditionalAttrParams(decl, "copyable_if:");
}

CxxEscapability
ClangTypeEscapability::evaluate(Evaluator &evaluator,
                                EscapabilityLookupDescriptor desc) const {

  // Escapability inference rules:
  // - array and vector types have the same escapability as their element type
  // - pointer and reference types are currently imported as unknown
  // (importing them as non-escapable broke backward compatibility)
  // - a record type is escapable or non-escapable if it is explicitly annotated
  // as such
  // - a record type is escapable if it is annotated with SWIFT_ESCAPABLE_IF()
  // and none of the annotation arguments are non-escapable
  // - an aggregate or non-cxx record is escapable if none of their fields or
  // bases are non-escapable (as long as they have a definition)
  //   * we only infer escapability for simple types, with no user-declared
  //   constructors, virtual bases or virtual member functions
  //   * for more complex CxxRecordDecls, we rely solely on escapability
  //   annotations
  // - in all other cases, the record has unknown escapability (e.g. no
  // escapability annotations, malformed escapability annotations)

  bool hasUnknown = false;