Skip to content

Conversation

@j-hui
Copy link
Contributor

@j-hui j-hui commented Nov 7, 2025

Prior to this patch, we eagerly imported template type arguments.
A consequence of doing so is that we over-instantiate (potentially
unused) type arguments, which causes unnecessary errors. This patch
skips importing those types.

These types were previously used to determine whether an imported
type is unsafe. This patch moves that check into the request that
checks for Clang decl safety.

rdar://145238539
rdar://163196609

@hnrklssn
Copy link
Member

hnrklssn commented Nov 7, 2025

Could you elaborate on the differences between this and the previous patches?

@j-hui j-hui marked this pull request as draft November 7, 2025 08:07
j-hui added 2 commits November 7, 2025 00:12
Checking this upon import may overlook annotations that explicitly
mark a type as safe (or unsafe). Instead, we consolidate this logic in
the ClangDeclExplicitSafety request.

rdar://163196609
Prior to this patch, we eagerly imported template type arguments.
A consequence of doing so is that we over-instantiate (potentially
unused) type arguments, which causes unnecessary errors.

The only apparent use we have for these type arguments are to check
whether they are unsafe, so that we can mark the instantiated type as
unsafe as well... even if the instantiated type does not use its unsafe
template type argument at all.

Using un-instantiatable types in template type arguments is supported
in C++. The test case included in this patch validates this behavior,
for both missing member and incomplete type errors.

Note, also, that as of this patch, dumping the module interface of
CxxModule actually causes swift-ide-test to emit compiler errors, since
it tries to instantiate the invalid types MissingMember<Empty> and
IncompleteField<Incomplete>. However, these errors are simply swallowed
by swift-ide-test, so they should be harmless, though we should probably
get rid of them entirely in future work.

rdar://145238539
@j-hui j-hui force-pushed the dont-import-template-type-arguments-round-2 branch from c24d10f to 3255ffb Compare November 7, 2025 08:13
@j-hui
Copy link
Contributor Author

j-hui commented Nov 7, 2025

This patch re-lands the patches reverted in #85296. Note that there were no known issues with that patch.

The main difference in this patch is that it eliminates the ClangTypeExplicitSafety request, which ended up being unnecessary because I no longer needed to expose the logic that checks types to the decl importer.

@j-hui
Copy link
Contributor Author

j-hui commented Nov 7, 2025

@swift-ci please test

@hnrklssn
Copy link
Member

hnrklssn commented Nov 7, 2025

The main difference in this patch is that it eliminates the ClangTypeExplicitSafety request, which ended up being unnecessary because I no longer needed to expose the logic that checks types to the decl importer.

Was it this request that caused the cycle mentioned in #85296, or was it some other patch that has now been fixed?

j-hui added 2 commits November 7, 2025 15:02
Doing so reduces opportunities for caching, but it's not obvious to me
that we benefit from such fine-grained caching. The benefit here is that
we also avoid the pitfalls of incremental caching, as illustrated by the
added test case. Finally, we eliminate the use of hasActiveRequest(),
which is an anti-pattern.
@j-hui j-hui marked this pull request as ready for review November 7, 2025 23:15
@j-hui j-hui requested a review from CodaFi as a code owner November 7, 2025 23:15
@j-hui
Copy link
Contributor Author

j-hui commented Nov 7, 2025

Was it this request that caused the cycle mentioned in #85296, or was it some other patch that has now been fixed?

This request was not at fault. The issue ended up being the patches reverted in #85331.

@j-hui
Copy link
Contributor Author

j-hui commented Nov 7, 2025

@swift-ci please test

@j-hui j-hui requested a review from DougGregor November 7, 2025 23:16
j-hui added 2 commits November 7, 2025 15:22
…icitSafetyDescriptor

Keep the naming convention consistent; this isn't specific to Cxx
@j-hui
Copy link
Contributor Author

j-hui commented Nov 7, 2025

@swift-ci please test

@egorzhdan egorzhdan added the c++ interop Feature: Interoperability with C++ label Nov 7, 2025
Comment on lines +8875 to +8876
// If desc.decl doesn't have a definition, safety is unspecified.
return ExplicitSafety::Unspecified;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a decl has a field that has unspecified safety, and another field that is explicitly unsafe, is ExplicitSafety::Unspecified the right value to return?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This early return only triggers when decl == desc.decl, i.e., when the "origin" decl of the request lacks a definition.

Here, if decl is from a field, it should not equal desc.decl.

@j-hui
Copy link
Contributor Author

j-hui commented Nov 8, 2025

TIMEOUT: lldb-shell :: Breakpoint/jit-loader_jitlink_elf.test (2475 of 2475)
******************** TEST 'lldb-shell :: Breakpoint/jit-loader_jitlink_elf.test' FAILED ********************
Exit Code: 1
Timeout: Reached timeout of 900 seconds

Seems unrelated

@j-hui
Copy link
Contributor Author

j-hui commented Nov 8, 2025

@swift-ci please test windows platform

stack.push_back(desc.decl);
seen.insert(desc.decl);
while (!stack.empty()) {
const clang::Decl *decl = stack.back();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we end up visiting the same decl multiple time across different requests?

I wonder if we should also cache the responses explicitly for all the decls that we visited and computed safety for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is what we used to do, but caching makes it quite difficult to do this correctly.

Consider:

template <typename, typename> struct TTake2 {};
template <typename T> struct PassThru {};

struct IsUnsafe { int *p; };
struct HasUnsafe : TTake2<PassThru<HasUnsafe>, IsUnsafe> {};
using AlsoUnsafe = PassThru<HasUnsafe>;

Upon visual inspection, we can see that HasUnsafe should be unsafe because it inherits from TTake2<PassThru<HasUnsafe>, IsUnsafe>, which is unsafe because its second template type parameter is unsafe. But we might not know that while we're checking the safety of the first template parameter, PassThru<HasUnsafe>.

When we look through PassThru<HasUnsafe> and arrive at its template parameter, we need to break the cycle and assume that safety is unspecified for the time being. We will later discover that HasUnsafe is actually unsafe via its second template parameter. However, if we are caching intermediate results, then we will incorrectly remember that PassThru<HasUnsafe> is unspecified, which is incorrect because it has an unsafe template parameter.

It's possible to make this work with conditional caching, but it is not clear to me that the added complexity is worth it.

@j-hui
Copy link
Contributor Author

j-hui commented Nov 9, 2025

Also unrelated:

********************
Failed Tests (1):
  lldb-api :: commands/platform/launchgdbserver/TestPlatformLaunchGDBServer.py

@j-hui
Copy link
Contributor Author

j-hui commented Nov 9, 2025

@swift-ci please test windows platform

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ interop Feature: Interoperability with C++

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants