Skip to content

[clang][Sema] Fix assertion in tryDiagnoseOverloadedCast #108021

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

Conversation

alejandro-alvarez-sonarsource
Copy link
Contributor

@alejandro-alvarez-sonarsource alejandro-alvarez-sonarsource commented Sep 10, 2024

Fixed an assertion failure in debug mode, and potential crashes in release mode, when
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.

For instance

template<typename>
struct StringTrait {};

template< int N >
struct StringTrait< const char[ N ] > {
  typedef char CharType;
  static const MissingIntT length = N - 1;
};

class String {
public:
  template <typename T>
  String(T& str, typename StringTrait<T>::CharType = 0);
};


class Exception {
public:
  Exception(String const&);
};

void foo() {
  throw Exception("some error");
}

Exception(String const&) is a matching constructor for Exception from a const char*, via an implicit conversion to String. However, the instantiation of the String constructor will fail because of the missing type MissingIntT inside the specialization of StringTrait.

When trying to emit a diagnosis, tryDiagnoseOverloadedCast expects not to have a matching constructor, but there is; it just could not be instantiated.

A constructor may have failed to be used due to a broken templated
dependent parameter. The `InitializationSequence` itself can
succeed.

Due to the assumption that it is in a failed state, it can
either cause an assertion failure, or undefined behavior in release
mode, since `sequence.Failure` is not initialized.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Sep 10, 2024
@alejandro-alvarez-sonarsource alejandro-alvarez-sonarsource changed the title [clang][Sema] Fix wrong assumption in tryDiagnoseOverloadedCast [clang][Sema] Fix assertion in tryDiagnoseOverloadedCast Sep 10, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 10, 2024

@llvm/pr-subscribers-clang

Author: Alejandro Álvarez Ayllón (alejandro-alvarez-sonarsource)

Changes

A constructor may have failed to be used due to a broken templated dependent parameter. The InitializationSequence itself can succeed.

Due to the assumption that it is in a failed state, it can either cause an assertion failure, or undefined behavior in release mode, since sequence.Failure is not initialized.


Full diff: https://github.com/llvm/llvm-project/pull/108021.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaCast.cpp (+6-1)
  • (added) clang/test/Parser/cxx-bad-cast-diagnose-broken-template.cpp (+31)
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index f01b22a72915c8..6ac6201843476b 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -446,7 +446,12 @@ static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT,
     : InitializationKind::CreateCast(/*type range?*/ range);
   InitializationSequence sequence(S, entity, initKind, src);
 
-  assert(sequence.Failed() && "initialization succeeded on second try?");
+  // It could happen that a constructor failed to be used because
+  // it requires a temporary of a broken type. Still, it will be found when
+  // looking for a match.
+  if (!sequence.Failed())
+    return false;
+
   switch (sequence.getFailureKind()) {
   default: return false;
 
diff --git a/clang/test/Parser/cxx-bad-cast-diagnose-broken-template.cpp b/clang/test/Parser/cxx-bad-cast-diagnose-broken-template.cpp
new file mode 100644
index 00000000000000..f9fc3e6082da10
--- /dev/null
+++ b/clang/test/Parser/cxx-bad-cast-diagnose-broken-template.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify %s
+
+template< typename>
+struct IsConstCharArray
+{
+  static const bool value = false;
+};
+
+template< int N >
+struct IsConstCharArray< const char[ N ] >
+{
+  typedef char CharType;
+  static const bool value = true;
+  static const missing_int_t length = N - 1; // expected-error {{unknown type name 'missing_int_t'}}
+};
+
+class String {
+public:
+  template <typename T>
+  String(T& str, typename IsConstCharArray<T>::CharType = 0);
+};
+
+
+class Exception {
+public:
+  Exception(String const&);
+};
+
+void foo() {
+  throw Exception("some error"); // expected-error {{functional-style cast from 'const char[11]' to 'Exception' is not allowed}}
+}

@shafik shafik requested review from cor3ntin and Endilll September 13, 2024 01:57
Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

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

Thank you for the fix.

Is this from a bug report, if so it should be mentioned in the summary.

The summary itself could be a big clearer, maybe a small code example could help.

I think this also needs a release note.

@alejandro-alvarez-sonarsource
Copy link
Contributor Author

I have tried to clarify the PR with a snippet and aligned the test with it.

Is this from a bug report, if so it should be mentioned in the summary.

It isn't. Or, as far as I know, there is no mention of it.

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

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

LGTM

@alejandro-alvarez-sonarsource
Copy link
Contributor Author

Thanks! Do you mind merging the PR? I do not have permission to do so.

@cor3ntin cor3ntin merged commit 0dd5685 into llvm:main Sep 18, 2024
9 checks passed
@alejandro-alvarez-sonarsource alejandro-alvarez-sonarsource deleted the aa/upstream/assertion_matching_constructor branch September 18, 2024 13:10
tmsri pushed a commit to tmsri/llvm-project that referenced this pull request Sep 19, 2024
Fixed an assertion failure in debug mode, and potential crashes in
release mode, when
diagnosing a failed cast caused indirectly by a failed implicit
conversion to the type of the constructor parameter.

For instance

```
template<typename>
struct StringTrait {};

template< int N >
struct StringTrait< const char[ N ] > {
  typedef char CharType;
  static const MissingIntT length = N - 1;
};

class String {
public:
  template <typename T>
  String(T& str, typename StringTrait<T>::CharType = 0);
};


class Exception {
public:
  Exception(String const&);
};

void foo() {
  throw Exception("some error");
}
```

`Exception(String const&)` is a matching constructor for `Exception`
from a `const char*`, via an implicit conversion to `String`. However,
the instantiation of the `String` constructor will fail because of the
missing type `MissingIntT` inside the specialization of `StringTrait`.

When trying to emit a diagnosis, `tryDiagnoseOverloadedCast` expects not
to have a matching constructor, but there is; it just could not be
instantiated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants